From 252a3ad625d509f2018d71a974a375de188dc0a9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 28 May 2021 16:39:30 +0300 Subject: [PATCH 01/75] UI: Clear code after upgrade Angular --- ...ecurity-config-lwm2m-server.component.scss | 20 ------------------- .../security-config-lwm2m-server.component.ts | 2 +- .../security-config-lwm2m.component.scss | 4 ---- 3 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss diff --git a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss deleted file mode 100644 index 1c55a86d8b..0000000000 --- a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.scss +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright © 2016-2021 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. - */ -:host ::ng-deep { - textarea.mat-input-element.cdk-textarea-autosize { - box-sizing: content-box; - } -} diff --git a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts index d599927834..09b7014212 100644 --- a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m-server.component.ts @@ -41,7 +41,7 @@ import { Subject } from 'rxjs'; @Component({ selector: 'tb-security-config-lwm2m-server', templateUrl: './security-config-lwm2m-server.component.html', - styleUrls: ['./security-config-lwm2m-server.component.scss'], + styleUrls: [], providers: [ { provide: NG_VALUE_ACCESSOR, diff --git a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss index 0188fcd559..393d2a9ef8 100644 --- a/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss +++ b/ui-ngx/src/app/modules/home/components/device/security-config-lwm2m.component.scss @@ -27,8 +27,4 @@ .mat-tab-body { padding: 16px 0; } - - textarea.mat-input-element.cdk-textarea-autosize { - box-sizing: content-box; - } } From 1b2a25919108fc5f55c0a534887e664cd7d138e0 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 28 May 2021 16:46:03 +0300 Subject: [PATCH 02/75] UI: Add unsubscribe to device profile transport type --- ...ofile-transport-configuration.component.ts | 21 ++++++++++---- .../device-profile-configuration.component.ts | 17 +++++++++-- ...ofile-transport-configuration.component.ts | 25 ++++++++++++----- ...ofile-transport-configuration.component.ts | 28 +++++++++++++------ .../device-profile-tabs.component.ts | 2 +- 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts index dc1da3d973..996e79d6bc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/coap-device-profile-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; @@ -33,6 +33,8 @@ import { transportPayloadTypeTranslationMap, } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-coap-device-profile-transport-configuration', @@ -44,7 +46,7 @@ import { isDefinedAndNotNull } from '@core/utils'; multi: true }] }) -export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { +export class CoapDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { coapTransportDeviceTypes = Object.keys(CoapTransportDeviceType); @@ -56,6 +58,7 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control coapDeviceProfileTransportConfigurationFormGroup: FormGroup; + private destroy$ = new Subject(); private requiredValue: boolean; private transportPayloadTypeConfiguration = this.fb.group({ @@ -99,15 +102,23 @@ export class CoapDeviceProfileTransportConfigurationComponent implements Control }) } ); - this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType') - .valueChanges.subscribe(coapDeviceType => { + this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(coapDeviceType => { this.updateCoapDeviceTypeBasedControls(coapDeviceType, true); }); - this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.coapDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + get coapDeviceTypeDefault(): boolean { const coapDeviceType = this.coapDeviceProfileTransportConfigurationFormGroup.get('coapDeviceTypeConfiguration.coapDeviceType').value; return coapDeviceType === CoapTransportDeviceType.DEFAULT; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts index b95433d096..27dda6dc91 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/device-profile-configuration.component.ts @@ -14,13 +14,15 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { Store } from '@ngrx/store'; import { AppState } from '@app/core/core.state'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceProfileConfiguration, DeviceProfileType } from '@shared/models/device.models'; import { deepClone } from '@core/utils'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-device-profile-configuration', @@ -32,12 +34,14 @@ import { deepClone } from '@core/utils'; multi: true }] }) -export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit { +export class DeviceProfileConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { deviceProfileType = DeviceProfileType; deviceProfileConfigurationFormGroup: FormGroup; + private destroy$ = new Subject(); + private requiredValue: boolean; get required(): boolean { return this.requiredValue; @@ -69,11 +73,18 @@ export class DeviceProfileConfigurationComponent implements ControlValueAccessor this.deviceProfileConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.deviceProfileConfigurationFormGroup.valueChanges.subscribe(() => { + this.deviceProfileConfigurationFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts index 381e2f9e8d..0cbb50ed8e 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/mqtt-device-profile-transport-configuration.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; import { ControlValueAccessor, FormBuilder, @@ -39,6 +39,8 @@ import { transportPayloadTypeTranslationMap } from '@shared/models/device.models'; import { isDefinedAndNotNull } from '@core/utils'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-mqtt-device-profile-transport-configuration', @@ -50,7 +52,7 @@ import { isDefinedAndNotNull } from '@core/utils'; multi: true }] }) -export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { +export class MqttDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { transportPayloadTypes = Object.keys(TransportPayloadType); @@ -58,6 +60,7 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control mqttDeviceProfileTransportConfigurationFormGroup: FormGroup; + private destroy$ = new Subject(); private requiredValue: boolean; get required(): boolean { @@ -98,15 +101,23 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control }) }, {validator: this.uniqueDeviceTopicValidator} ); - this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType') - .valueChanges.subscribe(payloadType => { + this.mqttDeviceProfileTransportConfigurationFormGroup.get('transportPayloadTypeConfiguration.transportPayloadType').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(payloadType => { this.updateTransportPayloadBasedControls(payloadType, true); }); - this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.mqttDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { @@ -192,8 +203,8 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control } private uniqueDeviceTopicValidator(control: FormGroup): { [key: string]: boolean } | null { - if (control.value) { - const formValue = control.value as MqttDeviceProfileTransportConfiguration; + if (control.getRawValue()) { + const formValue = control.getRawValue() as MqttDeviceProfileTransportConfiguration; if (formValue.deviceAttributesTopic === formValue.deviceTelemetryTopic) { return {unique: true}; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts index 96f7454cba..e6749a9219 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts @@ -14,17 +14,19 @@ /// limitations under the License. /// -import {Component, forwardRef, Input, OnInit} from '@angular/core'; -import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms'; -import {Store} from '@ngrx/store'; -import {AppState} from '@app/core/core.state'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import { Component, forwardRef, Input, OnDestroy, OnInit } from '@angular/core'; +import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@app/core/core.state'; +import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DeviceProfileTransportConfiguration, DeviceTransportType, SnmpDeviceProfileTransportConfiguration } from '@shared/models/device.models'; -import {isDefinedAndNotNull} from "@core/utils"; +import { isDefinedAndNotNull } from '@core/utils'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; export interface OidMappingConfiguration { isAttribute: boolean; @@ -44,8 +46,11 @@ export interface OidMappingConfiguration { multi: true }] }) -export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit { +export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit, OnDestroy { + snmpDeviceProfileTransportConfigurationFormGroup: FormGroup; + + private destroy$ = new Subject(); private requiredValue: boolean; private configuration = []; @@ -71,11 +76,18 @@ export class SnmpDeviceProfileTransportConfigurationComponent implements Control this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({ configuration: [null, Validators.required] }); - this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => { + this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + registerOnChange(fn: any): void { this.propagateChange = fn; } diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts index 804b349cff..2c53f04986 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profile-tabs.component.ts @@ -32,7 +32,7 @@ import { }) export class DeviceProfileTabsComponent extends EntityTabsComponent { - deviceTransportTypes = Object.keys(DeviceTransportType); + deviceTransportTypes = Object.values(DeviceTransportType); deviceTransportTypeTranslations = deviceTransportTypeTranslationMap; From ad68aa6d48ec29e4ac8e541e1e3b03f2655e1f44 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 28 May 2021 16:50:58 +0300 Subject: [PATCH 03/75] UI: Refactoring device profile transport LwM2M --- .../lwm2m-device-config-server.component.html | 209 +++++++++--------- ...ile-transport-configuration.component.html | 153 ++++++------- ...ofile-transport-configuration.component.ts | 40 +++- .../lwm2m/lwm2m-object-list.component.ts | 57 +++-- ...rve-attr-telemetry-resource.component.html | 6 +- ...wm2m-observe-attr-telemetry.component.html | 2 +- ...wm2m-observe-attr-telemetry.component.scss | 3 + .../lwm2m/lwm2m-profile-config.models.ts | 3 +- 8 files changed, 237 insertions(+), 236 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html index fdc02f910a..711b72d615 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-config-server.component.html @@ -15,112 +15,107 @@ limitations under the License. --> -
-
-
-
- - {{ 'device-profile.lwm2m.mode' | translate }} - - - {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} - - - - - {{ 'device-profile.lwm2m.server-host' | translate }} - - - {{ 'device-profile.lwm2m.server-host' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.server-port' | translate }} - - - {{ 'device-profile.lwm2m.server-port' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.short-id' | translate }} - - - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - -
-
-
-
- - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} - - - {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} - - - {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.bootstrap-server' | translate }} - -
-
- - {{ 'device-profile.lwm2m.server-public-key' | translate }} - - {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { - count: 0} }} - - - {{ 'device-profile.lwm2m.server-public-key' | translate }} - {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { - count: lenMaxServerPublicKey } }} - - -
-
+
+
+ + {{ 'device-profile.lwm2m.mode' | translate }} + + + {{ credentialTypeLwM2MNamesMap.get(securityConfigLwM2MType[securityMode]) }} + + + + + {{ 'device-profile.lwm2m.server-host' | translate }} + + + {{ 'device-profile.lwm2m.server-host' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.server-port' | translate }} + + + {{ 'device-profile.lwm2m.server-port' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.short-id' | translate }} + + + {{ 'device-profile.lwm2m.short-id' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} + + + {{ 'device-profile.lwm2m.client-hold-off-time' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} + + + {{ 'device-profile.lwm2m.bootstrap-server-account-timeout' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.bootstrap-server' | translate }} + +
+
+
+ + {{ 'device-profile.lwm2m.server-public-key' | translate }} + + {{serverPublicKey.value?.length || 0}}/{{lenMaxServerPublicKey}} + + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { + count: 0} }} + + + {{ 'device-profile.lwm2m.server-public-key' | translate }} + {{ 'device-profile.lwm2m.pattern_hex_dec' | translate: { + count: lenMaxServerPublicKey } }} + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index a29605a527..1a783aa4f5 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -15,12 +15,12 @@ limitations under the License. --> -
+
-
+
{{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }}
-
- - -
-
- - -
+ + + +
@@ -58,94 +54,73 @@ - -
{{ 'device-profile.lwm2m.servers' | translate | uppercase }}
-
+ {{ 'device-profile.lwm2m.servers' | translate }}
-
-
- - {{ 'device-profile.lwm2m.short-id' | translate }} - - - {{ 'device-profile.lwm2m.short-id' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - - - {{ 'device-profile.lwm2m.lifetime' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - - - {{ 'device-profile.lwm2m.default-min-period' | translate }} - {{ 'device-profile.lwm2m.required' | translate }} - - -
-
- - {{ 'device-profile.lwm2m.binding' | translate }} - - - {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} - - - -
-
- - {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} - -
+
+ + {{ 'device-profile.lwm2m.short-id' | translate }} + + + {{ 'device-profile.lwm2m.short-id' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.lifetime' | translate }} + + + {{ 'device-profile.lwm2m.lifetime' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + + + {{ 'device-profile.lwm2m.default-min-period' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + +
+ + {{ 'device-profile.lwm2m.binding' | translate }} + + + {{ bindingModeTypeNamesMap.get(bindingModeType[bindingMode]) }} + + + + + {{ 'device-profile.lwm2m.notif-if-disabled' | translate }} + - - - -
{{ 'device-profile.lwm2m.bootstrap-server' | translate | uppercase }}
-
+ {{ 'device-profile.lwm2m.bootstrap-server' | translate }}
-
- - -
+ +
-
- - -
{{ 'device-profile.lwm2m.lwm2m-server' | translate | uppercase }}
-
+ {{ 'device-profile.lwm2m.lwm2m-server' | translate }}
-
- - -
+ +
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 0a943ab5af..eda01c0584 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -15,7 +15,7 @@ /// import { DeviceProfileTransportConfiguration } from '@shared/models/device.models'; -import { Component, forwardRef, Input } from '@angular/core'; +import { Component, forwardRef, Input, OnDestroy } from '@angular/core'; import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { @@ -39,6 +39,8 @@ import { deepClone, isDefinedAndNotNull, isEmpty, isUndefined } from '@core/util import { JsonArray, JsonObject } from '@angular/compiler-cli/ngcc/src/packages/entry_point'; import { Direction } from '@shared/models/page/sort-order'; import _ from 'lodash'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -49,11 +51,12 @@ import _ from 'lodash'; multi: true }] }) -export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators { +export class Lwm2mDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, Validators, OnDestroy { private configurationValue: Lwm2mProfileConfigModels; private requiredValue: boolean; private disabled = false; + private destroy$ = new Subject(); bindingModeType = BINDING_MODE; bindingModeTypes = Object.keys(BINDING_MODE); @@ -87,17 +90,21 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro lifetime: [null, Validators.required], defaultMinPeriod: [null, Validators.required], notifIfDisabled: [true, []], - binding:[], + binding: [], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] }); - this.lwm2mDeviceProfileFormGroup.valueChanges.subscribe((value) => { + this.lwm2mDeviceProfileFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { this.updateDeviceProfileValue(value); }); - this.lwm2mDeviceConfigFormGroup.valueChanges.subscribe(() => { + this.lwm2mDeviceConfigFormGroup.valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe(() => { this.updateModel(); }); this.sortFunction = this.sortObjectKeyPathJson; @@ -110,6 +117,11 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro registerOnTouched(fn: any): void { } + ngOnDestroy() { + this.destroy$.next(); + this.destroy$.complete(); + } + setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { @@ -122,11 +134,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } writeValue(value: Lwm2mProfileConfigModels | null): void { - this.configurationValue = (Object.keys(value).length === 0) ? getDefaultProfileConfig() : value; - this.lwm2mDeviceConfigFormGroup.patchValue({ - configurationJson: this.configurationValue - }, {emitEvent: false}); - this.initWriteValue(); + if (isDefinedAndNotNull(value)) { + if (Object.keys(value).length !== 0 && (value?.clientLwM2mSettings || value?.observeAttr || value?.bootstrap)) { + this.configurationValue = value; + } else { + this.configurationValue = getDefaultProfileConfig(); + } + this.lwm2mDeviceConfigFormGroup.patchValue({ + configurationJson: this.configurationValue + }, {emitEvent: false}); + this.initWriteValue(); + } } private initWriteValue = (): void => { @@ -252,7 +270,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro instanceUpdate.id = instanceId; instanceUpdate.resources.forEach(resource => { resource.keyName = _.camelCase(resource.name + instanceUpdate.id); - }) + }); return instanceUpdate; } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts index 8f1f676769..ae7c3ccdc6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts @@ -15,10 +15,19 @@ /// import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; -import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; +import { + ControlValueAccessor, + FormBuilder, + FormGroup, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + ValidationErrors, + Validator, + Validators +} from '@angular/forms'; import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { Observable } from 'rxjs'; -import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; +import { distinctUntilChanged, filter, mergeMap, share, tap } from 'rxjs/operators'; import { ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT } from './lwm2m-profile-config.models'; import { DeviceProfileService } from '@core/http/device-profile.service'; import { Direction } from '@shared/models/page/sort-order'; @@ -33,13 +42,18 @@ import { PageLink } from '@shared/models/page/page-link'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Lwm2mObjectListComponent), multi: true - }] + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => Lwm2mObjectListComponent), + multi: true + } + ] }) -export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validators { +export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, Validator { private requiredValue: boolean; private dirty = false; - private lw2mModels: Observable>; private modelValue: Array = []; lwm2mListFormGroup: FormGroup; @@ -78,8 +92,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } private updateValidators = (): void => { - this.lwm2mListFormGroup.get('objectLwm2m').setValidators(this.required ? [Validators.required] : []); - this.lwm2mListFormGroup.get('objectLwm2m').updateValueAndValidity(); + this.lwm2mListFormGroup.get('objectsList').setValidators(this.required ? [Validators.required] : []); + this.lwm2mListFormGroup.get('objectsList').updateValueAndValidity(); } registerOnChange(fn: any): void { @@ -92,6 +106,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V ngOnInit() { this.filteredObjectsList = this.lwm2mListFormGroup.get('objectLwm2m').valueChanges .pipe( + distinctUntilChanged(), tap((value) => { if (value && typeof value !== 'string') { this.add(value); @@ -100,7 +115,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } }), filter(searchText => isString(searchText)), - mergeMap(searchText => this.fetchListObjects(searchText)) + mergeMap(searchText => this.fetchListObjects(searchText)), + share() ); } @@ -131,6 +147,12 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } } + validate(): ValidationErrors | null { + return this.lwm2mListFormGroup.valid ? null : { + lwm2mListObj: false + }; + } + private add(object: ObjectLwM2M): void { if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) { this.modelValue.push(object.keyId); @@ -157,23 +179,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V return object ? object.name : undefined; } - private fetchListObjects = (searchText?: string): Observable> => { + private fetchListObjects = (searchText: string): Observable> => { this.searchText = searchText; - return this.getLwM2mModelsPage().pipe( - map(objectLwM2Ms => objectLwM2Ms) - ); - } - - private getLwM2mModelsPage(): Observable> { const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, { property: 'id', direction: Direction.ASC }); - this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe( - publishReplay(1), - refCount() - ); - return this.lw2mModels; + return this.deviceProfileService.getLwm2mObjectsPage(pageLink); } onFocus = (): void => { @@ -183,10 +195,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V } } - private clear = (value: string = ''): void => { - this.objectInput.nativeElement.value = value; + private clear = (): void => { this.searchText = ''; - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(value); + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null); setTimeout(() => { this.objectInput.nativeElement.blur(); this.objectInput.nativeElement.focus(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html index e58861c1eb..3ee9bffa27 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry-resource.component.html @@ -15,7 +15,7 @@ limitations under the License. --> -
+
@@ -46,14 +46,14 @@
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html index 759e880ffa..02d594905d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html @@ -16,7 +16,7 @@ -->
- + diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss index 2de6fe17d6..afed0db1ff 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.scss @@ -24,6 +24,9 @@ } :host{ + section { + padding: 2px; + } .instance-list { mat-expansion-panel-header { color: inherit; diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index acea41394a..b719ca9ad6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -64,7 +64,7 @@ export const BINDING_MODE_NAMES = new Map( [BINDING_MODE.UQ, 'UQ: UDP connection in queue mode'], [BINDING_MODE.US, 'US: both UDP and SMS connections active, both in standard mode'], [BINDING_MODE.UQS, 'UQS: both UDP and SMS connections active; UDP in queue mode, SMS in standard mode'], - [BINDING_MODE.T,'T: TCP connection in standard mode'], + [BINDING_MODE.T, 'T: TCP connection in standard mode'], [BINDING_MODE.TQ, 'TQ: TCP connection in queue mode'], [BINDING_MODE.TS, 'TS: both TCP and SMS connections active, both in standard mode'], [BINDING_MODE.TQS, 'TQS: both TCP and SMS connections active; TCP in queue mode, SMS in standard mode'], @@ -162,7 +162,6 @@ export interface Lwm2mProfileConfigModels { clientLwM2mSettings: ClientLwM2mSettings; observeAttr: ObservableAttributes; bootstrap: BootstrapSecurityConfig; - } export interface ClientLwM2mSettings { From dfa4433ce97cb1636ee1c93d5f487cfc280e4833 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 30 May 2021 17:07:43 +0300 Subject: [PATCH 04/75] LWM2M: add logs --- .../DefaultLwM2MTransportMsgHandler.java | 61 ++++++++++++------- .../server/client/LwM2mClientContextImpl.java | 8 ++- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 3 +- .../lwm2m/utils/LwM2mValueConverterImpl.java | 3 + 4 files changed, 52 insertions(+), 23 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 9092f8abeb..1541ee8ef9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -352,7 +352,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); - if (msg.getSharedUpdatedCount() > 0) { + if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { + log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); @@ -387,7 +388,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } }); - } else if (msg.getSharedDeletedCount() > 0) { + } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); @@ -397,6 +398,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler }); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); } + else if (lwM2MClient == null) { + log.error ("OnAttributeUpdate, lwM2MClient is null"); + } } /** @@ -443,6 +447,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { // #1 this.checkRpcRequestTimeout(); + log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); @@ -502,6 +507,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { + log.warn ("5) nToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); transportService.process(sessionInfo, toDeviceResponse, null); } @@ -882,10 +888,16 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler */ private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); - ResourceModel.Type currentType = resourceValue.getType(); - ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); - return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); + if (resourceValue != null) { + ResourceModel.Type currentType = resourceValue.getType(); + ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); + return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, + new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); + } + + else { + return null; + } } /** @@ -1246,22 +1258,28 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler */ public void updateAttributeFromThingsboard(List tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); - tsKvProtos.forEach(tsKvProto -> { - String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey()); - if (pathIdVer != null) { - // #1.1 - if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) { - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); - } else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) { - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); + if (lwM2MClient != null) { + log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); + tsKvProtos.forEach(tsKvProto -> { + String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey()); + if (pathIdVer != null) { + // #1.1 + if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) { + lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); + } else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) { + lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); + } } - } - }); - // #2.1 - lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { - this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), - getValueFromKvProto(tsKvProto.getKv()), pathIdVer); - }); + }); + // #2.1 + lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { + this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), + getValueFromKvProto(tsKvProto.getKv()), pathIdVer); + }); + } + else { + log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); + } } /** @@ -1350,6 +1368,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(FirmwareType.FIRMWARE.name())) { + log.warn ("7) firmware start with ver: [{}]",response.getVersion()); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index fa43909498..8674516e85 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -83,11 +83,17 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) { - return lwM2mClientsByEndpoint.values().stream().filter(c -> + LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) ).findAny().get(); + if (lwM2mClient == null) { + log.warn("Device TimeOut? lwM2mClient is null."); + log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); + log.error("", new RuntimeException()); + } + return lwM2mClient; } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index e3e8608839..5084555378 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -155,12 +155,13 @@ public class LwM2mFwSwUpdate { */ private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name(); -// this.observeStateUpdate(); + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); int chunkSize = 0; int chunk = 0; byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); + log.warn ("8) firmware send save to : [{}]", targetIdVer); request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), firmwareChunk, handler.config.getTimeout(), null); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java index acc6ea1857..9a36b79051 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/utils/LwM2mValueConverterImpl.java @@ -43,6 +43,9 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { @Override public Object convertValue(Object value, Type currentType, Type expectedType, LwM2mPath resourcePath) throws CodecException { + if (value == null) { + return null; + } if (expectedType == null) { /** unknown resource, trusted value */ return value; From b0f5ff7c639a8ed5e39aeb420d25d5b810778281 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 31 May 2021 11:10:24 +0300 Subject: [PATCH 05/75] LWM2M: add RPC FirmwareUpdate --- .../DefaultLwM2MTransportMsgHandler.java | 42 +++-- .../lwm2m/server/LwM2mTransportRequest.java | 148 ++++++++++-------- .../lwm2m/server/LwM2mTransportUtil.java | 5 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 43 +++-- .../server/client/Lwm2mClientRpcRequest.java | 4 +- 5 files changed, 146 insertions(+), 96 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 1541ee8ef9..6b5188cbfb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -196,8 +196,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) .build(); transportService.process(msg, null); - this.getInfoFirmwareUpdate(lwM2MClient); - this.getInfoSoftwareUpdate(lwM2MClient); + this.getInfoFirmwareUpdate(lwM2MClient, null); + this.getInfoSoftwareUpdate(lwM2MClient, null); this.initLwM2mFromClientValue(registration, lwM2MClient); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); } else { @@ -286,7 +286,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void setCancelObservationsAll(Registration registration) { if (registration != null) { - lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL, + this.lwM2mTransportRequest.sendAllRequest(registration, null, OBSERVE_CANCEL_ALL, null, null, this.config.getTimeout(), null); } } @@ -335,7 +335,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler READ, pathIdVer, value); this.sendLogsToThingsboard(msg, registration.getId()); rpcRequest.setValueMsg(String.format("%s", value)); - this.sentRpcRequest(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); + this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); } /** @@ -362,12 +362,12 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { - this.getInfoFirmwareUpdate(lwM2MClient); + this.getInfoFirmwareUpdate(lwM2MClient, null); } else if ((FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) || (FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { - this.getInfoSoftwareUpdate(lwM2MClient); + this.getInfoSoftwareUpdate(lwM2MClient, null); } if (pathIdVer != null) { ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config @@ -484,7 +484,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); } - public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { + public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { rpcRequest.setResponseCode(requestCode); if (LOG_LW2M_ERROR.equals(typeMsg)) { rpcRequest.setInfoMsg(null); @@ -507,7 +507,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { - log.warn ("5) nToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); transportService.process(sessionInfo, toDeviceResponse, null); } @@ -1358,21 +1358,28 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) { + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { - transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()), + DefaultLwM2MTransportMsgHandler handler = this; + this.transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()), new TransportServiceCallback<>() { @Override public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(FirmwareType.FIRMWARE.name())) { - log.warn ("7) firmware start with ver: [{}]",response.getVersion()); + log.warn ("7) firmware start with ver: [{}]", response.getVersion()); + lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); - lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + if (rpcRequest == null) { + lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + } + else { + lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + } } else { log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); } @@ -1387,21 +1394,28 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient) { + public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { - DefaultLwM2MTransportMsgHandler serviceImpl = this; + DefaultLwM2MTransportMsgHandler handler = this; transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.SOFTWARE.name()), new TransportServiceCallback<>() { @Override public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(FirmwareType.SOFTWARE.name())) { + lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + if (rpcRequest == null) { + lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + } + else { + lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + } } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index e5cd289758..31276077b0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -82,10 +82,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; @@ -134,7 +132,7 @@ public class LwM2mTransportRequest { ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; LwM2mClient lwM2MClient = this.lwM2mClientContext.getOrRegister(registration); LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; - if (!OBSERVE_READ_ALL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { + if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { if (lwM2MClient.isValidObjectVersion(targetIdVer)) { timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; DownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, @@ -153,47 +151,66 @@ public class LwM2mTransportRequest { } else if (WRITE_UPDATE.name().equals(typeOper.name())) { if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s params is not valid", targetIdVer); - handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); } } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s object model is absent", targetIdVer); - handler.sentRpcRequest(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); } } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); if (lwm2mClientRpcRequest != null) { ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; - this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } } else if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s not found in object version", targetIdVer); - this.handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } - } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_ALL.name().equals(typeOper.name())) { - Set paths; - if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { - Set observations = context.getServer().getObservationService().getObservations(registration); - paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); - } else { - assert registration != null; - Link[] objectLinks = registration.getSortedObjectLinks(); - paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); - } - String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, - typeOper.name(), paths); - this.handler.sendLogsToThingsboard(msg, registration.getId()); - if (lwm2mClientRpcRequest != null) { - String valueMsg = String.format("Paths - %s", paths); - this.handler.sentRpcRequest(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); + } else { + switch (typeOper) { + case OBSERVE_READ_ALL: + case DISCOVER_ALL: + Set paths; + if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { + Set observations = context.getServer().getObservationService().getObservations(registration); + paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); + } else { + assert registration != null; + Link[] objectLinks = registration.getSortedObjectLinks(); + paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); + } + String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, + typeOper.name(), paths); + this.handler.sendLogsToThingsboard(msg, registration.getId()); + if (lwm2mClientRpcRequest != null) { + String valueMsg = String.format("Paths - %s", paths); + this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); + } + break; + case OBSERVE_CANCEL: + case OBSERVE_CANCEL_ALL: + int observeCancelCnt = 0; + String observeCancelMsg = null; + if (OBSERVE_CANCEL.name().equals(typeOper)) { + observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); + observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, + OBSERVE_CANCEL.name(), target, observeCancelCnt); + } else { + observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); + observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, + OBSERVE_CANCEL.name(), observeCancelCnt); + } + this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); + break; + // lwm2mClientRpcRequest != null + case FW_UPDATE: + this.handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); + break; } - } else if (OBSERVE_CANCEL_ALL.name().equals(typeOper.name())) { - int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); - String observeCancelMsgAll = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, - OBSERVE_CANCEL.name(), observeCancelCnt); - this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsgAll, lwm2mClientRpcRequest); } } catch (Exception e) { String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, @@ -201,7 +218,7 @@ public class LwM2mTransportRequest { handler.sendLogsToThingsboard(msg, registration.getId()); if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); - handler.sentRpcRequest(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } } @@ -234,17 +251,6 @@ public class LwM2mTransportRequest { request = new ObserveRequest(contentFormat, resultIds.getObjectId()); } break; - case OBSERVE_CANCEL: - /* - lwM2MTransportRequest.sendAllRequest(lwServer, registration, path, POST_TYPE_OPER_OBSERVE_CANCEL, null, null, null, null, context.getTimeout()); - At server side this will not remove the observation from the observation store, to do it you need to use - {@code ObservationService#cancelObservation()} - */ - int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); - String observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, - OBSERVE_CANCEL.name(), target, observeCancelCnt); - this.afterObserveCancel(registration, observeCancelCnt, observeCancelMsg, rpcRequest); - break; case EXECUTE: ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); if (resourceModelExecute != null) { @@ -343,7 +349,7 @@ public class LwM2mTransportRequest { } /** Not Found */ if (rpcRequest != null) { - handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); } /** Not Found set setClient_fw_info... = empty @@ -385,7 +391,7 @@ public class LwM2mTransportRequest { handler.sendLogsToThingsboard(msg, registration.getId()); log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); if (rpcRequest != null) { - handler.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); + handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); } }); } @@ -431,7 +437,7 @@ public class LwM2mTransportRequest { log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); if (rpcRequest != null) { String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); - handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); } return null; } @@ -462,44 +468,48 @@ public class LwM2mTransportRequest { if (response instanceof ReadResponse) { handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); } else if (response instanceof DeleteResponse) { - log.warn("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response); + log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response); + if (rpcRequest != null) { + rpcRequest.setInfoMsg(null); + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null); + } } else if (response instanceof DiscoverResponse) { - String discoverValue = Link.serialize(((DiscoverResponse)response).getObjectLinks()); + String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); msgLog = String.format("%s: type operation: %s path: %s value: %s", LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); handler.sendLogsToThingsboard(msgLog, registration.getId()); log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); if (rpcRequest != null) { - handler.sentRpcRequest(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); } } else if (response instanceof ExecuteResponse) { - log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response); + msgLog = String.format("%s: type operation: %s path: %s", + LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); + log.warn("9) [{}] ", msgLog); + handler.sendLogsToThingsboard(msgLog, registration.getId()); + if (rpcRequest != null) { + msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); + rpcRequest.setInfoMsg(msgLog); + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO); + } + } else if (response instanceof WriteAttributesResponse) { msgLog = String.format("%s: type operation: %s path: %s value: %s", LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); handler.sendLogsToThingsboard(msgLog, registration.getId()); - log.warn("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); + log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); if (rpcRequest != null) { - handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); } } else if (response instanceof WriteResponse) { - log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response); - this.infoWriteResponse(registration, response, request); + msgLog = String.format("Type operation: Write path: %s", pathIdVer); + log.warn("10) [{}] response: [{}]", msgLog, response); + this.infoWriteResponse(registration, response, request, rpcRequest); handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); } - if (rpcRequest != null) { - if (response instanceof ExecuteResponse - || response instanceof WriteAttributesResponse - || response instanceof DeleteResponse) { - rpcRequest.setInfoMsg(null); - handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); - } else if (response instanceof WriteResponse) { - handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO); - } - } } - private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request) { + private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { try { LwM2mNode node = ((WriteRequest) request).getNode(); String msg = null; @@ -517,12 +527,12 @@ public class LwM2mTransportRequest { if (singleResource.getType() == ResourceModel.Type.STRING) { valueLength = ((String) singleResource.getValue()).length(); value = ((String) singleResource.getValue()) - .substring(Math.min(valueLength, config.getLogMaxLength())); + .substring(Math.min(valueLength, config.getLogMaxLength())).trim(); } else { valueLength = ((byte[]) singleResource.getValue()).length; value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), - Math.min(valueLength, config.getLogMaxLength()))); + Math.min(valueLength, config.getLogMaxLength()))).trim(); } value = valueLength > config.getLogMaxLength() ? value + "..." : value; msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", @@ -538,6 +548,12 @@ public class LwM2mTransportRequest { handler.sendLogsToThingsboard(msg, registration.getId()); if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteSuccessFwSwUpdate(registration, request); + if (rpcRequest != null) { + rpcRequest.setInfoMsg(msg); + } + } + else if (rpcRequest != null) { + handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); } } } catch (Exception e) { @@ -558,7 +574,7 @@ public class LwM2mTransportRequest { } if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name()); - lwM2MClient.getSwUpdate().sendLogs(this.handler,WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } } @@ -592,7 +608,7 @@ public class LwM2mTransportRequest { log.warn("[{}]", observeCancelMsg); if (rpcRequest != null) { rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); - handler.sentRpcRequest(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); + handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); } } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 320059b0a7..bc64574f8c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -229,11 +229,12 @@ public class LwM2mTransportUtil { */ WRITE_UPDATE(9, "WriteUpdate"), WRITE_ATTRIBUTES(10, "WriteAttributes"), - DELETE(11, "Delete"); + DELETE(11, "Delete"), // only for RPC + FW_UPDATE(12,"FirmwareUpdate"); // FW_READ_INFO(12, "FirmwareReadInfo"), -// FW_UPDATE(13, "FirmwareUpdate"), + // SW_READ_INFO(15, "SoftwareReadInfo"), // SW_UPDATE(16, "SoftwareUpdate"), // SW_UNINSTALL(18, "SoftwareUninstall"); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 5084555378..a36c1353ac 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; +import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; @@ -103,6 +104,9 @@ public class LwM2mFwSwUpdate { @Getter @Setter private final List pendingInfoRequestsStart; + @Getter + @Setter + private volatile Lwm2mClientRpcRequest rpcRequest; public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, FirmwareType type) { this.lwM2MClient = lwM2MClient; @@ -153,17 +157,30 @@ public class LwM2mFwSwUpdate { * Send FsSw to Lwm2mClient: * before operation Write: fw_state = DOWNLOADING */ - private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { - this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name(); - - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); - int chunkSize = 0; - int chunk = 0; - byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk); - String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); - log.warn ("8) firmware send save to : [{}]", targetIdVer); - request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), - firmwareChunk, handler.config.getTimeout(), null); + public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { + if (this.currentId != null) { + this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name(); + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); + int chunkSize = 0; + int chunk = 0; + byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk); + String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); + String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, + LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID); + handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); + log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); + request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), + firmwareChunk, handler.config.getTimeout(), this.rpcRequest); + } + else { + String msgError = "FirmWareId is null."; + log.warn("6) [{}]", msgError); + if (this.rpcRequest != null) { + handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); + } + log.error (msgError); + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); + } } public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) { @@ -186,7 +203,7 @@ public class LwM2mFwSwUpdate { this.setStateUpdate(UPDATING.name()); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(), - null, 0, null); + null, 0, this.rpcRequest); } /** @@ -348,7 +365,7 @@ public class LwM2mFwSwUpdate { this.pathResultId, this.lwM2MClient.getRegistration())); this.pendingInfoRequestsStart.forEach(pathIdVer -> { request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(), - null, 0, null); + null, 0, this.rpcRequest); }); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java index b31f841065..d71c6b27f6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java @@ -39,6 +39,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.K import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; @@ -140,7 +141,8 @@ public class Lwm2mClientRpcRequest { if (this.getTargetIdVer() == null && !(OBSERVE_READ_ALL == this.getTypeOper() || DISCOVER_ALL == this.getTypeOper() - || OBSERVE_CANCEL == this.getTypeOper())) { + || OBSERVE_CANCEL == this.getTypeOper() + || FW_UPDATE == this.getTypeOper())) { this.setErrorMsg(TARGET_ID_VER_KEY + " and " + KEY_NAME_KEY + " is null or bad format"); } From 9bb74b96dca6921d3b29f4e44992f698c2d1ca40 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 26 May 2021 15:20:15 +0300 Subject: [PATCH 06/75] Implement alarms removal by TTL --- .../ttl/alarms/AlarmsCleanUpService.java | 83 +++++++++++++++++++ .../src/main/resources/thingsboard.yml | 3 + .../server/common/data/TenantProfile.java | 7 ++ .../server/common/data/page/PageData.java | 7 +- .../DefaultTenantProfileConfiguration.java | 1 + .../java/org/thingsboard/server/dao/Dao.java | 3 + .../server/dao/alarm/AlarmDao.java | 4 + .../server/dao/sql/JpaAbstractDao.java | 7 ++ .../server/dao/sql/alarm/AlarmRepository.java | 5 ++ .../server/dao/sql/alarm/JpaAlarmDao.java | 6 ++ .../server/dao/sql/tenant/JpaTenantDao.java | 6 ++ .../dao/sql/tenant/TenantRepository.java | 4 + .../server/dao/tenant/TenantDao.java | 4 +- 13 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java new file mode 100644 index 0000000000..fe89af7faa --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java @@ -0,0 +1,83 @@ +/** + * Copyright © 2016-2021 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.service.ttl.alarms; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; +import org.thingsboard.server.common.msg.queue.ServiceType; +import org.thingsboard.server.dao.alarm.AlarmDao; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; +import org.thingsboard.server.dao.tenant.TenantDao; +import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.queue.discovery.PartitionService; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeUnit; + +@PsqlDao +@Service +@Slf4j +@RequiredArgsConstructor +public class AlarmsCleanUpService { + @Value("${sql.ttl.alarms.removal_batch_size}") + private Integer removalBatchSize; + + private final AlarmDao alarmDao; + private final TenantDao tenantDao; + private final PartitionService partitionService; + private final TbTenantProfileCache tenantProfileCache; + + @Scheduled(initialDelayString = "${sql.ttl.alarms.checking_interval}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") + public void cleanUp() { + if (!partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition()) { + return; + } + + PageLink tenantsBatchRequest = new PageLink(65536, 0); + PageLink alarmsRemovalBatchRequest = new PageLink(removalBatchSize, 0); + long currentTime = System.currentTimeMillis(); + + PageData tenantsIds; + do { + tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); + tenantsIds.getData().forEach(tenantId -> { + Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { + return; + } + + PageData toRemove; + long outdatageTime = currentTime - TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); + log.info("Cleaning up outdated alarms for tenant {}", tenantId); + do { + toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, alarmsRemovalBatchRequest); + alarmDao.removeAllByIds(toRemove.getData()); + } while (toRemove.hasNext()); + }); + + tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); + } while (tenantsIds.hasNext()); + } + +} diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 1cc89feab2..8edff5366e 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -273,6 +273,9 @@ sql: enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month + alarms: + checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours + removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:200}" # To delete outdated alarms not all at once but in batches # Actor system parameters actors: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index a0fefea6cc..e3bb6c6a4e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.validation.NoXss; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.Optional; import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo.mapper; @@ -92,6 +93,12 @@ public class TenantProfile extends SearchTextBased implements H } } + public Optional getProfileConfiguration() { + return Optional.ofNullable(getProfileData().getConfiguration()) + .filter(profileConfiguration -> profileConfiguration instanceof DefaultTenantProfileConfiguration) + .map(profileConfiguration -> (DefaultTenantProfileConfiguration) profileConfiguration); + } + public TenantProfileData createDefaultTenantProfileData() { TenantProfileData tpd = new TenantProfileData(); tpd.setConfiguration(new DefaultTenantProfileConfiguration()); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java index 2020245ea1..6ffbce4d3d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/page/PageData.java @@ -17,10 +17,11 @@ package org.thingsboard.server.common.data.page; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.thingsboard.server.common.data.BaseData; import java.util.Collections; import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; public class PageData { @@ -61,4 +62,8 @@ public class PageData { return hasNext; } + public PageData mapData(Function mapper) { + return new PageData<>(getData().stream().map(mapper).collect(Collectors.toList()), getTotalPages(), getTotalElements(), hasNext()); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index b9bd72b0db..34f0b38ebf 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -53,6 +53,7 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private long maxCreatedAlarms; private int defaultStorageTtlDays; + private int alarmsTtlDays; private double warnThreshold; diff --git a/dao/src/main/java/org/thingsboard/server/dao/Dao.java b/dao/src/main/java/org/thingsboard/server/dao/Dao.java index 0111abdbbe..a5d4dfd9d1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/Dao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/Dao.java @@ -18,6 +18,7 @@ package org.thingsboard.server.dao; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.TenantId; +import java.util.Collection; import java.util.List; import java.util.UUID; @@ -33,4 +34,6 @@ public interface Dao { boolean removeById(TenantId tenantId, UUID id); + void removeAllByIds(Collection ids); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index eb873db679..bcef402ff9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -25,6 +25,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.Dao; @@ -54,4 +55,7 @@ public interface AlarmDao extends Dao { AlarmDataQuery query, Collection orderedEntityIds); Set findAlarmSeverities(TenantId tenantId, EntityId entityId, Set status); + + PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 6bad1af0e5..18852cbf11 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -26,6 +26,7 @@ import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.BaseEntity; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -87,6 +88,12 @@ public abstract class JpaAbstractDao, D> return !getCrudRepository().existsById(id); } + @Transactional + public void removeAllByIds(Collection ids) { + CrudRepository repository = getCrudRepository(); + ids.forEach(repository::deleteById); + } + @Override public List find(TenantId tenantId) { List entities = Lists.newArrayList(getCrudRepository().findAll()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index b4c0ac09c6..b6eef91ac7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -17,6 +17,7 @@ package org.thingsboard.server.dao.sql.alarm; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; @@ -159,4 +160,8 @@ public interface AlarmRepository extends CrudRepository { @Param("affectedEntityId") UUID affectedEntityId, @Param("affectedEntityType") String affectedEntityType, @Param("alarmStatuses") Set alarmStatuses); + + @Query("SELECT a.id FROM AlarmEntity a WHERE a.createdTime < :time AND a.endTs < :time") + Page findAlarmsIdsByEndTsBefore(@Param("time") Long time, Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index cf222413b0..f7da17d6ed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.AlarmData; import org.thingsboard.server.common.data.query.AlarmDataQuery; import org.thingsboard.server.dao.DaoUtil; @@ -161,4 +162,9 @@ public class JpaAlarmDao extends JpaAbstractDao implements A public Set findAlarmSeverities(TenantId tenantId, EntityId entityId, Set statuses) { return alarmRepository.findAlarmSeverities(tenantId.getId(), entityId.getId(), entityId.getEntityType().name(), statuses); } + + @Override + public PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(alarmRepository.findAlarmsIdsByEndTsBefore(time, DaoUtil.toPageable(pageLink))); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java index d2a67d50ce..ff9dce6c96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/JpaTenantDao.java @@ -74,4 +74,10 @@ public class JpaTenantDao extends JpaAbstractSearchTextDao Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink, TenantInfoEntity.tenantInfoColumnMap))); } + + @Override + public PageData findTenantsIds(PageLink pageLink) { + return DaoUtil.pageToPageData(tenantRepository.findTenantsIds(DaoUtil.toPageable(pageLink))).mapData(TenantId::new); + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java index b43d70197c..8ab12e0bb5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/tenant/TenantRepository.java @@ -50,4 +50,8 @@ public interface TenantRepository extends PagingAndSortingRepository findTenantInfoByRegionNextPage(@Param("region") String region, @Param("textSearch") String textSearch, Pageable pageable); + + @Query("SELECT t.id FROM TenantEntity t") + Page findTenantsIds(Pageable pageable); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java index bff1c2c8a9..5bceb35376 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantDao.java @@ -46,5 +46,7 @@ public interface TenantDao extends Dao { PageData findTenantsByRegion(TenantId tenantId, String region, PageLink pageLink); PageData findTenantInfosByRegion(TenantId tenantId, String region, PageLink pageLink); - + + PageData findTenantsIds(PageLink pageLink); + } From 6c6f9b20ae1bb4412e7e08793456ce02012b951d Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Thu, 27 May 2021 10:55:33 +0300 Subject: [PATCH 07/75] Alarms TTL UI --- .../ttl/alarms/AlarmsCleanUpService.java | 32 +++++++++---------- ...enant-profile-configuration.component.html | 12 +++++++ ...-tenant-profile-configuration.component.ts | 3 +- .../assets/locale/locale.constant-en_US.json | 3 ++ 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java index fe89af7faa..08b08a742b 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java @@ -50,10 +50,6 @@ public class AlarmsCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.alarms.checking_interval}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") public void cleanUp() { - if (!partitionService.resolve(ServiceType.TB_CORE, TenantId.SYS_TENANT_ID, TenantId.SYS_TENANT_ID).isMyPartition()) { - return; - } - PageLink tenantsBatchRequest = new PageLink(65536, 0); PageLink alarmsRemovalBatchRequest = new PageLink(removalBatchSize, 0); long currentTime = System.currentTimeMillis(); @@ -61,20 +57,22 @@ public class AlarmsCleanUpService { PageData tenantsIds; do { tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); - tenantsIds.getData().forEach(tenantId -> { - Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); - if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { - return; - } + tenantsIds.getData().stream() + .filter(tenantId -> partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) + .forEach(tenantId -> { + Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { + return; + } - PageData toRemove; - long outdatageTime = currentTime - TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); - log.info("Cleaning up outdated alarms for tenant {}", tenantId); - do { - toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, alarmsRemovalBatchRequest); - alarmDao.removeAllByIds(toRemove.getData()); - } while (toRemove.hasNext()); - }); + PageData toRemove; + long outdatageTime = currentTime - TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); + log.info("Cleaning up outdated alarms for tenant {}", tenantId); + do { + toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, alarmsRemovalBatchRequest); + alarmDao.removeAllByIds(toRemove.getData()); + } while (toRemove.hasNext()); + }); tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); } while (tenantsIds.hasNext()); diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 13e3976299..4efe5d124f 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -160,6 +160,18 @@ {{ 'tenant-profile.default-storage-ttl-days-range' | translate}} + + tenant-profile.alarms-ttl-days + + + {{ 'tenant-profile.alarms-ttl-days-required' | translate}} + + + {{ 'tenant-profile.alarms-ttl-days-days-range' | translate}} + + tenant-profile.max-rule-node-executions-per-message { this.updateModel(); diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 3da7b4f4bb..b1f65780f5 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2522,6 +2522,9 @@ "default-storage-ttl-days": "Default storage TTL days (0 - unlimited)", "default-storage-ttl-days-required": "Default storage TTL days is required.", "default-storage-ttl-days-range": "Default storage TTL days can't be negative", + "alarms-ttl-days": "Alarms TTL days (0 - unlimited)", + "alarms-ttl-days-required": "Alarms TTL days required", + "alarms-ttl-days-days-range": "Alarms TTL days can't be negative", "max-rule-node-executions-per-message": "Maximum number of rule node executions per message (0 - unlimited)", "max-rule-node-executions-per-message-required": "Maximum number of rule node executions per message is required.", "max-rule-node-executions-per-message-range": "Maximum number of rule node executions per message can't be negative", From 9e1d86d7e8c4837738d5ce696c5cc88b78edee16 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Mon, 31 May 2021 15:41:16 +0300 Subject: [PATCH 08/75] Refactor --- .../server/controller/AlarmController.java | 5 +- .../server/controller/BaseController.java | 212 +-------------- .../action/RuleEngineEntityActionService.java | 256 ++++++++++++++++++ .../service/ttl/AbstractCleanUpService.java | 6 +- .../ttl/alarms/AlarmsCleanUpService.java | 67 +++-- .../ttl/edge/EdgeEventsCleanUpService.java | 2 +- .../ttl/events/EventsCleanUpService.java | 2 +- .../AbstractTimeseriesCleanUpService.java | 2 +- .../src/main/resources/thingsboard.yml | 2 +- .../server/common/data/DataConstants.java | 1 + .../server/common/data/TenantProfile.java | 1 + .../server/common/data/audit/ActionType.java | 1 + .../server/dao/alarm/AlarmDao.java | 3 +- .../server/dao/sql/alarm/AlarmRepository.java | 5 +- .../server/dao/sql/alarm/JpaAlarmDao.java | 6 +- .../rule/engine/profile/DeviceState.java | 8 + 16 files changed, 341 insertions(+), 238 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java index 03883fc167..f0af59c7bc 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AlarmController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AlarmController.java @@ -110,8 +110,11 @@ public class AlarmController extends BaseController { checkParameter(ALARM_ID, strAlarmId); try { AlarmId alarmId = new AlarmId(toUUID(strAlarmId)); - checkAlarmId(alarmId, Operation.WRITE); + Alarm alarm = checkAlarmId(alarmId, Operation.WRITE); + logEntityAction(alarm.getOriginator(), alarm, + getCurrentUser().getCustomerId(), + ActionType.ALARM_DELETE, null); sendEntityNotificationMsg(getTenantId(), alarmId, EdgeEventActionType.DELETED); return alarmService.deleteAlarm(getTenantId(), alarmId); diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 505416f4d6..5ccb763445 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -17,7 +17,6 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -31,7 +30,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Dashboard; import org.thingsboard.server.common.data.DashboardInfo; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; @@ -79,10 +77,6 @@ import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.DataType; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -94,9 +88,6 @@ import org.thingsboard.server.common.data.rule.RuleChainType; import org.thingsboard.server.common.data.rule.RuleNode; import org.thingsboard.server.common.data.widget.WidgetTypeDetails; import org.thingsboard.server.common.data.widget.WidgetsBundle; -import org.thingsboard.server.common.msg.TbMsg; -import org.thingsboard.server.common.msg.TbMsgDataType; -import org.thingsboard.server.common.msg.TbMsgMetaData; import org.thingsboard.server.dao.asset.AssetService; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.audit.AuditLogService; @@ -127,6 +118,7 @@ import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.action.RuleEngineEntityActionService; import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.firmware.FirmwareStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; @@ -147,11 +139,9 @@ import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import javax.mail.MessagingException; import javax.servlet.http.HttpServletResponse; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.UUID; -import java.util.stream.Collectors; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -281,6 +271,9 @@ public abstract class BaseController { @Autowired(required = false) protected EdgeGrpcService edgeGrpcService; + @Autowired + protected RuleEngineEntityActionService ruleEngineEntityActionService; + @Value("${server.log_controller_error_stack_trace}") @Getter private boolean logControllerErrorStackTrace; @@ -811,7 +804,7 @@ public abstract class BaseController { customerId = user.getCustomerId(); } if (e == null) { - pushEntityActionToRuleEngine(entityId, entity, user, customerId, actionType, additionalInfo); + ruleEngineEntityActionService.pushEntityActionToRuleEngine(entityId, entity, user.getTenantId(), customerId, actionType, user, additionalInfo); } auditLogService.logEntityAction(user.getTenantId(), customerId, user.getId(), user.getName(), entityId, entity, actionType, e, additionalInfo); } @@ -821,184 +814,6 @@ public abstract class BaseController { return error != null ? (Exception.class.isInstance(error) ? (Exception) error : new Exception(error)) : null; } - private void pushEntityActionToRuleEngine(I entityId, E entity, User user, CustomerId customerId, - ActionType actionType, Object... additionalInfo) { - String msgType = null; - switch (actionType) { - case ADDED: - msgType = DataConstants.ENTITY_CREATED; - break; - case DELETED: - msgType = DataConstants.ENTITY_DELETED; - break; - case UPDATED: - msgType = DataConstants.ENTITY_UPDATED; - break; - case ASSIGNED_TO_CUSTOMER: - msgType = DataConstants.ENTITY_ASSIGNED; - break; - case UNASSIGNED_FROM_CUSTOMER: - msgType = DataConstants.ENTITY_UNASSIGNED; - break; - case ATTRIBUTES_UPDATED: - msgType = DataConstants.ATTRIBUTES_UPDATED; - break; - case ATTRIBUTES_DELETED: - msgType = DataConstants.ATTRIBUTES_DELETED; - break; - case ALARM_ACK: - msgType = DataConstants.ALARM_ACK; - break; - case ALARM_CLEAR: - msgType = DataConstants.ALARM_CLEAR; - break; - case ASSIGNED_FROM_TENANT: - msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; - break; - case ASSIGNED_TO_TENANT: - msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; - break; - case PROVISION_SUCCESS: - msgType = DataConstants.PROVISION_SUCCESS; - break; - case PROVISION_FAILURE: - msgType = DataConstants.PROVISION_FAILURE; - break; - case TIMESERIES_UPDATED: - msgType = DataConstants.TIMESERIES_UPDATED; - break; - case TIMESERIES_DELETED: - msgType = DataConstants.TIMESERIES_DELETED; - break; - case ASSIGNED_TO_EDGE: - msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; - break; - case UNASSIGNED_FROM_EDGE: - msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; - break; - } - if (!StringUtils.isEmpty(msgType)) { - try { - TbMsgMetaData metaData = new TbMsgMetaData(); - metaData.putValue("userId", user.getId().toString()); - metaData.putValue("userName", user.getName()); - if (customerId != null && !customerId.isNullUid()) { - metaData.putValue("customerId", customerId.toString()); - } - if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { - String strCustomerId = extractParameter(String.class, 1, additionalInfo); - String strCustomerName = extractParameter(String.class, 2, additionalInfo); - metaData.putValue("assignedCustomerId", strCustomerId); - metaData.putValue("assignedCustomerName", strCustomerName); - } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { - String strCustomerId = extractParameter(String.class, 1, additionalInfo); - String strCustomerName = extractParameter(String.class, 2, additionalInfo); - metaData.putValue("unassignedCustomerId", strCustomerId); - metaData.putValue("unassignedCustomerName", strCustomerName); - } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { - String strTenantId = extractParameter(String.class, 0, additionalInfo); - String strTenantName = extractParameter(String.class, 1, additionalInfo); - metaData.putValue("assignedFromTenantId", strTenantId); - metaData.putValue("assignedFromTenantName", strTenantName); - } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { - String strTenantId = extractParameter(String.class, 0, additionalInfo); - String strTenantName = extractParameter(String.class, 1, additionalInfo); - metaData.putValue("assignedToTenantId", strTenantId); - metaData.putValue("assignedToTenantName", strTenantName); - } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { - String strEdgeId = extractParameter(String.class, 1, additionalInfo); - String strEdgeName = extractParameter(String.class, 2, additionalInfo); - metaData.putValue("assignedEdgeId", strEdgeId); - metaData.putValue("assignedEdgeName", strEdgeName); - } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { - String strEdgeId = extractParameter(String.class, 1, additionalInfo); - String strEdgeName = extractParameter(String.class, 2, additionalInfo); - metaData.putValue("unassignedEdgeId", strEdgeId); - metaData.putValue("unassignedEdgeName", strEdgeName); - } - ObjectNode entityNode; - if (entity != null) { - entityNode = json.valueToTree(entity); - if (entityId.getEntityType() == EntityType.DASHBOARD) { - entityNode.put("configuration", ""); - } - } else { - entityNode = json.createObjectNode(); - if (actionType == ActionType.ATTRIBUTES_UPDATED) { - String scope = extractParameter(String.class, 0, additionalInfo); - @SuppressWarnings("unchecked") - List attributes = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); - if (attributes != null) { - for (AttributeKvEntry attr : attributes) { - addKvEntry(entityNode, attr); - } - } - } else if (actionType == ActionType.ATTRIBUTES_DELETED) { - String scope = extractParameter(String.class, 0, additionalInfo); - @SuppressWarnings("unchecked") - List keys = extractParameter(List.class, 1, additionalInfo); - metaData.putValue(DataConstants.SCOPE, scope); - ArrayNode attrsArrayNode = entityNode.putArray("attributes"); - if (keys != null) { - keys.forEach(attrsArrayNode::add); - } - } else if (actionType == ActionType.TIMESERIES_UPDATED) { - @SuppressWarnings("unchecked") - List timeseries = extractParameter(List.class, 0, additionalInfo); - addTimeseries(entityNode, timeseries); - } else if (actionType == ActionType.TIMESERIES_DELETED) { - @SuppressWarnings("unchecked") - List keys = extractParameter(List.class, 0, additionalInfo); - if (keys != null) { - ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); - keys.forEach(timeseriesArrayNode::add); - } - entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); - entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); - } - } - TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); - TenantId tenantId = user.getTenantId(); - if (tenantId.isNullUid()) { - if (entity instanceof HasTenantId) { - tenantId = ((HasTenantId) entity).getTenantId(); - } - } - tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); - } catch (Exception e) { - log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); - } - } - } - - private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { - if (kvEntry.getDataType() == DataType.BOOLEAN) { - kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); - } else if (kvEntry.getDataType() == DataType.DOUBLE) { - kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); - } else if (kvEntry.getDataType() == DataType.LONG) { - kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); - } else if (kvEntry.getDataType() == DataType.JSON) { - if (kvEntry.getJsonValue().isPresent()) { - entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); - } - } else { - entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); - } - } - - private T extractParameter(Class clazz, int index, Object... additionalInfo) { - T result = null; - if (additionalInfo != null && additionalInfo.length > index) { - Object paramObject = additionalInfo[index]; - if (clazz.isInstance(paramObject)) { - result = clazz.cast(paramObject); - } - } - return result; - } - protected String entityToStr(E entity) { try { return json.writeValueAsString(json.valueToTree(entity)); @@ -1095,23 +910,6 @@ public abstract class BaseController { return result; } - private void addTimeseries(ObjectNode entityNode, List timeseries) throws Exception { - if (timeseries != null && !timeseries.isEmpty()) { - ArrayNode result = entityNode.putArray("timeseries"); - Map> groupedTelemetry = timeseries.stream() - .collect(Collectors.groupingBy(TsKvEntry::getTs)); - for (Map.Entry> entry : groupedTelemetry.entrySet()) { - ObjectNode element = json.createObjectNode(); - element.put("ts", entry.getKey()); - ObjectNode values = element.putObject("values"); - for (TsKvEntry tsKvEntry : entry.getValue()) { - addKvEntry(values, tsKvEntry); - } - result.add(element); - } - } - } - protected void processDashboardIdFromAdditionalInfo(ObjectNode additionalInfo, String requiredFields) throws ThingsboardException { String dashboardId = additionalInfo.has(requiredFields) ? additionalInfo.get(requiredFields).asText() : null; if (dashboardId != null && !dashboardId.equals("null")) { diff --git a/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java new file mode 100644 index 0000000000..f1320d1a7a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/action/RuleEngineEntityActionService.java @@ -0,0 +1,256 @@ +/** + * Copyright © 2016-2021 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.service.action; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.msg.TbMsg; +import org.thingsboard.server.common.msg.TbMsgDataType; +import org.thingsboard.server.common.msg.TbMsgMetaData; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.queue.TbClusterService; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@TbCoreComponent +@Service +@RequiredArgsConstructor +@Slf4j +public class RuleEngineEntityActionService { + private final TbClusterService tbClusterService; + + private static final ObjectMapper json = new ObjectMapper(); + + public void pushEntityActionToRuleEngine(EntityId entityId, HasName entity, TenantId tenantId, CustomerId customerId, + ActionType actionType, User user, Object... additionalInfo) { + String msgType = null; + switch (actionType) { + case ADDED: + msgType = DataConstants.ENTITY_CREATED; + break; + case DELETED: + msgType = DataConstants.ENTITY_DELETED; + break; + case UPDATED: + msgType = DataConstants.ENTITY_UPDATED; + break; + case ASSIGNED_TO_CUSTOMER: + msgType = DataConstants.ENTITY_ASSIGNED; + break; + case UNASSIGNED_FROM_CUSTOMER: + msgType = DataConstants.ENTITY_UNASSIGNED; + break; + case ATTRIBUTES_UPDATED: + msgType = DataConstants.ATTRIBUTES_UPDATED; + break; + case ATTRIBUTES_DELETED: + msgType = DataConstants.ATTRIBUTES_DELETED; + break; + case ALARM_ACK: + msgType = DataConstants.ALARM_ACK; + break; + case ALARM_CLEAR: + msgType = DataConstants.ALARM_CLEAR; + break; + case ALARM_DELETE: + msgType = DataConstants.ALARM_DELETE; + break; + case ASSIGNED_FROM_TENANT: + msgType = DataConstants.ENTITY_ASSIGNED_FROM_TENANT; + break; + case ASSIGNED_TO_TENANT: + msgType = DataConstants.ENTITY_ASSIGNED_TO_TENANT; + break; + case PROVISION_SUCCESS: + msgType = DataConstants.PROVISION_SUCCESS; + break; + case PROVISION_FAILURE: + msgType = DataConstants.PROVISION_FAILURE; + break; + case TIMESERIES_UPDATED: + msgType = DataConstants.TIMESERIES_UPDATED; + break; + case TIMESERIES_DELETED: + msgType = DataConstants.TIMESERIES_DELETED; + break; + case ASSIGNED_TO_EDGE: + msgType = DataConstants.ENTITY_ASSIGNED_TO_EDGE; + break; + case UNASSIGNED_FROM_EDGE: + msgType = DataConstants.ENTITY_UNASSIGNED_FROM_EDGE; + break; + } + if (!StringUtils.isEmpty(msgType)) { + try { + TbMsgMetaData metaData = new TbMsgMetaData(); + if (user != null) { + metaData.putValue("userId", user.getId().toString()); + metaData.putValue("userName", user.getName()); + } + if (customerId != null && !customerId.isNullUid()) { + metaData.putValue("customerId", customerId.toString()); + } + if (actionType == ActionType.ASSIGNED_TO_CUSTOMER) { + String strCustomerId = extractParameter(String.class, 1, additionalInfo); + String strCustomerName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("assignedCustomerId", strCustomerId); + metaData.putValue("assignedCustomerName", strCustomerName); + } else if (actionType == ActionType.UNASSIGNED_FROM_CUSTOMER) { + String strCustomerId = extractParameter(String.class, 1, additionalInfo); + String strCustomerName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("unassignedCustomerId", strCustomerId); + metaData.putValue("unassignedCustomerName", strCustomerName); + } else if (actionType == ActionType.ASSIGNED_FROM_TENANT) { + String strTenantId = extractParameter(String.class, 0, additionalInfo); + String strTenantName = extractParameter(String.class, 1, additionalInfo); + metaData.putValue("assignedFromTenantId", strTenantId); + metaData.putValue("assignedFromTenantName", strTenantName); + } else if (actionType == ActionType.ASSIGNED_TO_TENANT) { + String strTenantId = extractParameter(String.class, 0, additionalInfo); + String strTenantName = extractParameter(String.class, 1, additionalInfo); + metaData.putValue("assignedToTenantId", strTenantId); + metaData.putValue("assignedToTenantName", strTenantName); + } else if (actionType == ActionType.ASSIGNED_TO_EDGE) { + String strEdgeId = extractParameter(String.class, 1, additionalInfo); + String strEdgeName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("assignedEdgeId", strEdgeId); + metaData.putValue("assignedEdgeName", strEdgeName); + } else if (actionType == ActionType.UNASSIGNED_FROM_EDGE) { + String strEdgeId = extractParameter(String.class, 1, additionalInfo); + String strEdgeName = extractParameter(String.class, 2, additionalInfo); + metaData.putValue("unassignedEdgeId", strEdgeId); + metaData.putValue("unassignedEdgeName", strEdgeName); + } + ObjectNode entityNode; + if (entity != null) { + entityNode = json.valueToTree(entity); + if (entityId.getEntityType() == EntityType.DASHBOARD) { + entityNode.put("configuration", ""); + } + } else { + entityNode = json.createObjectNode(); + if (actionType == ActionType.ATTRIBUTES_UPDATED) { + String scope = extractParameter(String.class, 0, additionalInfo); + @SuppressWarnings("unchecked") + List attributes = extractParameter(List.class, 1, additionalInfo); + metaData.putValue(DataConstants.SCOPE, scope); + if (attributes != null) { + for (AttributeKvEntry attr : attributes) { + addKvEntry(entityNode, attr); + } + } + } else if (actionType == ActionType.ATTRIBUTES_DELETED) { + String scope = extractParameter(String.class, 0, additionalInfo); + @SuppressWarnings("unchecked") + List keys = extractParameter(List.class, 1, additionalInfo); + metaData.putValue(DataConstants.SCOPE, scope); + ArrayNode attrsArrayNode = entityNode.putArray("attributes"); + if (keys != null) { + keys.forEach(attrsArrayNode::add); + } + } else if (actionType == ActionType.TIMESERIES_UPDATED) { + @SuppressWarnings("unchecked") + List timeseries = extractParameter(List.class, 0, additionalInfo); + addTimeseries(entityNode, timeseries); + } else if (actionType == ActionType.TIMESERIES_DELETED) { + @SuppressWarnings("unchecked") + List keys = extractParameter(List.class, 0, additionalInfo); + if (keys != null) { + ArrayNode timeseriesArrayNode = entityNode.putArray("timeseries"); + keys.forEach(timeseriesArrayNode::add); + } + entityNode.put("startTs", extractParameter(Long.class, 1, additionalInfo)); + entityNode.put("endTs", extractParameter(Long.class, 2, additionalInfo)); + } + } + TbMsg tbMsg = TbMsg.newMsg(msgType, entityId, customerId, metaData, TbMsgDataType.JSON, json.writeValueAsString(entityNode)); + if (tenantId.isNullUid()) { + if (entity instanceof HasTenantId) { + tenantId = ((HasTenantId) entity).getTenantId(); + } + } + tbClusterService.pushMsgToRuleEngine(tenantId, entityId, tbMsg, null); + } catch (Exception e) { + log.warn("[{}] Failed to push entity action to rule engine: {}", entityId, actionType, e); + } + } + } + + + private T extractParameter(Class clazz, int index, Object... additionalInfo) { + T result = null; + if (additionalInfo != null && additionalInfo.length > index) { + Object paramObject = additionalInfo[index]; + if (clazz.isInstance(paramObject)) { + result = clazz.cast(paramObject); + } + } + return result; + } + + private void addTimeseries(ObjectNode entityNode, List timeseries) throws Exception { + if (timeseries != null && !timeseries.isEmpty()) { + ArrayNode result = entityNode.putArray("timeseries"); + Map> groupedTelemetry = timeseries.stream() + .collect(Collectors.groupingBy(TsKvEntry::getTs)); + for (Map.Entry> entry : groupedTelemetry.entrySet()) { + ObjectNode element = json.createObjectNode(); + element.put("ts", entry.getKey()); + ObjectNode values = element.putObject("values"); + for (TsKvEntry tsKvEntry : entry.getValue()) { + addKvEntry(values, tsKvEntry); + } + result.add(element); + } + } + } + + private void addKvEntry(ObjectNode entityNode, KvEntry kvEntry) throws Exception { + if (kvEntry.getDataType() == DataType.BOOLEAN) { + kvEntry.getBooleanValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); + } else if (kvEntry.getDataType() == DataType.DOUBLE) { + kvEntry.getDoubleValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); + } else if (kvEntry.getDataType() == DataType.LONG) { + kvEntry.getLongValue().ifPresent(value -> entityNode.put(kvEntry.getKey(), value)); + } else if (kvEntry.getDataType() == DataType.JSON) { + if (kvEntry.getJsonValue().isPresent()) { + entityNode.set(kvEntry.getKey(), json.readTree(kvEntry.getJsonValue().get())); + } + } else { + entityNode.put(kvEntry.getKey(), kvEntry.getValueAsString()); + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java index 95731d2988..05799fc643 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/AbstractCleanUpService.java @@ -17,9 +17,9 @@ package org.thingsboard.server.service.ttl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; -import org.thingsboard.server.dao.util.PsqlDao; import java.sql.Connection; +import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLWarning; @@ -62,4 +62,8 @@ public abstract class AbstractCleanUpService { protected abstract void doCleanUp(Connection connection) throws SQLException; + protected Connection getConnection() throws SQLException { + return DriverManager.getConnection(dbUrl, dbUserName, dbPassword); + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java index 08b08a742b..7d6ebb6939 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java @@ -20,17 +20,27 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.alarm.AlarmDao; +import org.thingsboard.server.dao.alarm.AlarmService; +import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.service.action.RuleEngineEntityActionService; +import org.thingsboard.server.service.ttl.AbstractCleanUpService; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Date; import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -43,37 +53,54 @@ public class AlarmsCleanUpService { @Value("${sql.ttl.alarms.removal_batch_size}") private Integer removalBatchSize; - private final AlarmDao alarmDao; private final TenantDao tenantDao; + private final AlarmDao alarmDao; + private final AlarmService alarmService; + private final RelationService relationService; + private final RuleEngineEntityActionService ruleEngineEntityActionService; private final PartitionService partitionService; private final TbTenantProfileCache tenantProfileCache; - @Scheduled(initialDelayString = "${sql.ttl.alarms.checking_interval}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") + @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.alarms.checking_interval})}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") public void cleanUp() { - PageLink tenantsBatchRequest = new PageLink(65536, 0); - PageLink alarmsRemovalBatchRequest = new PageLink(removalBatchSize, 0); - long currentTime = System.currentTimeMillis(); - + PageLink tenantsBatchRequest = new PageLink(10_000, 0); + PageLink removalBatchRequest = new PageLink(removalBatchSize, 0); PageData tenantsIds; do { tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); - tenantsIds.getData().stream() - .filter(tenantId -> partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) - .forEach(tenantId -> { - Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); - if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { - return; - } + for (TenantId tenantId : tenantsIds.getData()) { + if (!partitionService.resolve(ServiceType.TB_CORE, tenantId, tenantId).isMyPartition()) { + continue; + } + + Optional tenantProfileConfiguration = tenantProfileCache.get(tenantId).getProfileConfiguration(); + if (tenantProfileConfiguration.isEmpty() || tenantProfileConfiguration.get().getAlarmsTtlDays() == 0) { + continue; + } - PageData toRemove; - long outdatageTime = currentTime - TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); - log.info("Cleaning up outdated alarms for tenant {}", tenantId); - do { - toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, alarmsRemovalBatchRequest); - alarmDao.removeAllByIds(toRemove.getData()); - } while (toRemove.hasNext()); + long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); + long outdatageTime = System.currentTimeMillis() - ttl; + + long totalRemoved = 0; + while (true) { + PageData toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, removalBatchRequest); + toRemove.getData().forEach(alarmId -> { + relationService.deleteEntityRelations(tenantId, alarmId); + Alarm alarm = alarmService.deleteAlarm(tenantId, alarmId).getAlarm(); + ruleEngineEntityActionService.pushEntityActionToRuleEngine(alarm.getOriginator(), alarm, tenantId, null, ActionType.ALARM_DELETE, null); }); + totalRemoved += toRemove.getTotalElements(); + if (!toRemove.hasNext()) { + break; + } + } + + if (totalRemoved > 0) { + log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(outdatageTime)); + } + } + tenantsBatchRequest = tenantsBatchRequest.nextPageLink(); } while (tenantsIds.hasNext()); } diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java index 0c21719451..e93a82c7eb 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/edge/EdgeEventsCleanUpService.java @@ -40,7 +40,7 @@ public class EdgeEventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled) { - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + try (Connection conn = getConnection()) { doCleanUp(conn); } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java index 664e01e227..407c88261f 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/events/EventsCleanUpService.java @@ -43,7 +43,7 @@ public class EventsCleanUpService extends AbstractCleanUpService { @Scheduled(initialDelayString = "${sql.ttl.events.execution_interval_ms}", fixedDelayString = "${sql.ttl.events.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled) { - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + try (Connection conn = getConnection()) { doCleanUp(conn); } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java index 9ece0b91a2..ee2d437a22 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/timeseries/AbstractTimeseriesCleanUpService.java @@ -36,7 +36,7 @@ public abstract class AbstractTimeseriesCleanUpService extends AbstractCleanUpSe @Scheduled(initialDelayString = "${sql.ttl.ts.execution_interval_ms}", fixedDelayString = "${sql.ttl.ts.execution_interval_ms}") public void cleanUp() { if (ttlTaskExecutionEnabled) { - try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + try (Connection conn = getConnection()) { doCleanUp(conn); } catch (SQLException e) { log.error("SQLException occurred during TTL task execution ", e); diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 8edff5366e..8229046851 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -275,7 +275,7 @@ sql: edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month alarms: checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours - removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:200}" # To delete outdated alarms not all at once but in batches + removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches # Actor system parameters actors: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 62459fc0ad..002cbbb733 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -66,6 +66,7 @@ public class DataConstants { public static final String TIMESERIES_DELETED = "TIMESERIES_DELETED"; public static final String ALARM_ACK = "ALARM_ACK"; public static final String ALARM_CLEAR = "ALARM_CLEAR"; + public static final String ALARM_DELETE = "ALARM_DELETE"; public static final String ENTITY_ASSIGNED_FROM_TENANT = "ENTITY_ASSIGNED_FROM_TENANT"; public static final String ENTITY_ASSIGNED_TO_TENANT = "ENTITY_ASSIGNED_TO_TENANT"; public static final String PROVISION_SUCCESS = "PROVISION_SUCCESS"; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java index e3bb6c6a4e..05d22af09f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/TenantProfile.java @@ -93,6 +93,7 @@ public class TenantProfile extends SearchTextBased implements H } } + @JsonIgnore public Optional getProfileConfiguration() { return Optional.ofNullable(getProfileData().getConfiguration()) .filter(profileConfiguration -> profileConfiguration instanceof DefaultTenantProfileConfiguration) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index 2594c73ebe..489c45f68d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java @@ -39,6 +39,7 @@ public enum ActionType { RELATIONS_DELETED(false), ALARM_ACK(false), ALARM_CLEAR(false), + ALARM_DELETE(false), LOGIN(false), LOGOUT(false), LOCKOUT(false), diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java index bcef402ff9..3b10f624d7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -56,6 +57,6 @@ public interface AlarmDao extends Dao { Set findAlarmSeverities(TenantId tenantId, EntityId entityId, Set status); - PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); + PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java index b6eef91ac7..8f862390c3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/AlarmRepository.java @@ -23,6 +23,7 @@ import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.sql.AlarmEntity; import org.thingsboard.server.dao.model.sql.AlarmInfoEntity; @@ -161,7 +162,7 @@ public interface AlarmRepository extends CrudRepository { @Param("affectedEntityType") String affectedEntityType, @Param("alarmStatuses") Set alarmStatuses); - @Query("SELECT a.id FROM AlarmEntity a WHERE a.createdTime < :time AND a.endTs < :time") - Page findAlarmsIdsByEndTsBefore(@Param("time") Long time, Pageable pageable); + @Query("SELECT a.id FROM AlarmEntity a WHERE a.tenantId = :tenantId AND a.createdTime < :time AND a.endTs < :time") + Page findAlarmsIdsByEndTsBeforeAndTenantId(@Param("time") Long time, @Param("tenantId") UUID tenantId, Pageable pageable); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java index f7da17d6ed..3216a23eee 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -164,7 +165,8 @@ public class JpaAlarmDao extends JpaAbstractDao implements A } @Override - public PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink) { - return DaoUtil.pageToPageData(alarmRepository.findAlarmsIdsByEndTsBefore(time, DaoUtil.toPageable(pageLink))); + public PageData findAlarmsIdsByEndTsBeforeAndTenantId(Long time, TenantId tenantId, PageLink pageLink) { + return DaoUtil.pageToPageData(alarmRepository.findAlarmsIdsByEndTsBeforeAndTenantId(time, tenantId.getId(), DaoUtil.toPageable(pageLink))) + .mapData(AlarmId::new); } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index e84beae702..69614f8079 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -150,6 +150,8 @@ class DeviceState { stateChanged = processAlarmClearNotification(ctx, msg); } else if (msg.getType().equals(DataConstants.ALARM_ACK)) { processAlarmAckNotification(ctx, msg); + } else if (msg.getType().equals(DataConstants.ALARM_DELETE)) { + processAlarmDeleteNotification(ctx, msg); } else { if (msg.getType().equals(DataConstants.ENTITY_ASSIGNED) || msg.getType().equals(DataConstants.ENTITY_UNASSIGNED)) { dynamicPredicateValueCtx.resetCustomer(); @@ -193,6 +195,12 @@ class DeviceState { ctx.tellSuccess(msg); } + private void processAlarmDeleteNotification(TbContext ctx, TbMsg msg) { + Alarm alarm = JacksonUtil.fromString(msg.getData(), Alarm.class); + alarmStates.values().removeIf(alarmState -> alarmState.getCurrentAlarm().getId().equals(alarm.getId())); + ctx.tellSuccess(msg); + } + private boolean processAttributesUpdateNotification(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException { String scope = msg.getMetaData().getValue(DataConstants.SCOPE); if (StringUtils.isEmpty(scope)) { From a7239c9d39ac9f28faa0377f14823b6b46dea5dc Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 31 May 2021 16:43:33 +0300 Subject: [PATCH 09/75] Renamed Firmware to OtaPackage --- .../main/data/upgrade/3.2.2/schema_update.sql | 14 +- .../server/controller/BaseController.java | 42 +- .../server/controller/DeviceController.java | 19 +- .../controller/DeviceProfileController.java | 2 +- .../server/controller/FirmwareController.java | 222 ---------- .../controller/OtaPackageController.java | 222 ++++++++++ .../DefaultOtaPackageStateService.java} | 136 +++---- .../OtaPackageStateService.java} | 8 +- .../queue/DefaultTbCoreConsumerService.java | 31 +- .../service/security/AccessValidator.java | 24 +- .../service/security/permission/Resource.java | 2 +- .../permission/TenantAdminPermissions.java | 2 +- .../transport/DefaultTransportApiService.java | 68 ++-- .../src/main/resources/thingsboard.yml | 16 +- ...java => BaseOtaPackageControllerTest.java} | 115 +++--- ....java => OtaPackageControllerSqlTest.java} | 4 +- .../CaffeineOtaPackageCache.java} | 12 +- .../OtaPackageDataCache.java} | 8 +- .../RedisOtaPackageDataCache.java} | 18 +- .../server/dao/device/DeviceService.java | 5 +- .../server/dao/firmware/FirmwareService.java | 52 --- .../server/dao/ota/OtaPackageService.java | 52 +++ .../server/common/data/CacheConstants.java | 2 +- .../server/common/data/Device.java | 16 +- .../server/common/data/DeviceProfile.java | 8 +- .../server/common/data/EntityType.java | 2 +- .../{HasFirmware.java => HasOtaPackage.java} | 8 +- .../data/{Firmware.java => OtaPackage.java} | 10 +- ...{FirmwareInfo.java => OtaPackageInfo.java} | 40 +- .../common/data/id/EntityIdFactory.java | 4 +- .../id/{FirmwareId.java => OtaPackageId.java} | 10 +- .../{firmware => ota}/ChecksumAlgorithm.java | 2 +- .../OtaPackageKey.java} | 6 +- .../OtaPackageType.java} | 6 +- .../OtaPackageUpdateStatus.java} | 4 +- .../OtaPackageUtil.java} | 49 ++- .../queue/kafka/TbKafkaTopicConfigs.java | 2 +- .../provider/AwsSqsMonolithQueueFactory.java | 10 +- .../provider/AwsSqsTbCoreQueueFactory.java | 12 +- .../InMemoryMonolithQueueFactory.java | 8 +- .../provider/KafkaMonolithQueueFactory.java | 22 +- .../provider/KafkaTbCoreQueueFactory.java | 22 +- .../provider/PubSubMonolithQueueFactory.java | 12 +- .../provider/PubSubTbCoreQueueFactory.java | 12 +- .../RabbitMqMonolithQueueFactory.java | 12 +- .../provider/RabbitMqTbCoreQueueFactory.java | 12 +- .../ServiceBusMonolithQueueFactory.java | 12 +- .../ServiceBusTbCoreQueueFactory.java | 12 +- .../queue/provider/TbCoreQueueFactory.java | 6 +- .../provider/TbCoreQueueProducerProvider.java | 4 +- .../queue/settings/TbQueueCoreSettings.java | 4 +- common/queue/src/main/proto/queue.proto | 18 +- .../transport/coap/CoapTransportResource.java | 22 +- .../transport/http/DeviceApiController.java | 32 +- .../DefaultLwM2MTransportMsgHandler.java | 52 +-- .../lwm2m/server/LwM2mTransportRequest.java | 4 +- .../lwm2m/server/LwM2mTransportUtil.java | 48 +-- .../lwm2m/server/client/LwM2mClient.java | 6 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 28 +- .../transport/mqtt/MqttTransportHandler.java | 58 +-- .../mqtt/adaptors/JsonMqttAdaptor.java | 4 +- .../mqtt/adaptors/MqttTransportAdaptor.java | 4 +- .../mqtt/adaptors/ProtoMqttAdaptor.java | 4 +- .../common/transport/TransportContext.java | 5 +- .../common/transport/TransportService.java | 6 +- .../service/DefaultTransportService.java | 6 +- .../server/dao/device/DeviceDao.java | 8 +- .../dao/device/DeviceProfileServiceImpl.java | 16 +- .../server/dao/device/DeviceServiceImpl.java | 45 ++- .../server/dao/entity/BaseEntityService.java | 12 +- .../dao/firmware/BaseFirmwareService.java | 379 ------------------ .../server/dao/model/ModelConstants.java | 31 +- .../dao/model/sql/AbstractDeviceEntity.java | 6 +- .../dao/model/sql/DeviceProfileEntity.java | 6 +- ...mwareEntity.java => OtaPackageEntity.java} | 70 ++-- ...oEntity.java => OtaPackageInfoEntity.java} | 72 ++-- .../server/dao/ota/BaseOtaPackageService.java | 373 +++++++++++++++++ .../OtaPackageDao.java} | 6 +- .../OtaPackageInfoDao.java} | 18 +- .../dao/sql/device/DeviceRepository.java | 20 +- .../server/dao/sql/device/JpaDeviceDao.java | 36 +- .../sql/firmware/FirmwareInfoRepository.java | 60 --- .../dao/sql/firmware/JpaFirmwareInfoDao.java | 94 ----- .../JpaOtaPackageDao.java} | 20 +- .../dao/sql/ota/JpaOtaPackageInfoDao.java | 94 +++++ .../dao/sql/ota/OtaPackageInfoRepository.java | 60 +++ .../OtaPackageRepository.java} | 6 +- .../server/dao/tenant/TenantServiceImpl.java | 6 +- .../resources/sql/schema-entities-hsql.sql | 14 +- .../main/resources/sql/schema-entities.sql | 14 +- .../dao/service/AbstractServiceTest.java | 4 +- .../service/BaseDeviceProfileServiceTest.java | 11 +- .../dao/service/BaseDeviceServiceTest.java | 14 +- ...st.java => BaseOtaPackageServiceTest.java} | 214 +++++----- ...est.java => OtaPackageServiceSqlTest.java} | 4 +- .../resources/application-test.properties | 4 +- .../resources/sql/hsql/drop-all-tables.sql | 2 +- 97 files changed, 1722 insertions(+), 1697 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/controller/FirmwareController.java create mode 100644 application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java rename application/src/main/java/org/thingsboard/server/service/{firmware/DefaultFirmwareStateService.java => ota/DefaultOtaPackageStateService.java} (69%) rename application/src/main/java/org/thingsboard/server/service/{firmware/FirmwareStateService.java => ota/OtaPackageStateService.java} (79%) rename application/src/test/java/org/thingsboard/server/controller/{BaseFirmwareControllerTest.java => BaseOtaPackageControllerTest.java} (66%) rename application/src/test/java/org/thingsboard/server/controller/sql/{FirmwareControllerSqlTest.java => OtaPackageControllerSqlTest.java} (82%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/CaffeineFirmwareCache.java => ota/CaffeineOtaPackageCache.java} (84%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/FirmwareDataCache.java => ota/OtaPackageDataCache.java} (82%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/RedisFirmwareDataCache.java => ota/RedisOtaPackageDataCache.java} (78%) delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java rename common/data/src/main/java/org/thingsboard/server/common/data/{HasFirmware.java => HasOtaPackage.java} (80%) rename common/data/src/main/java/org/thingsboard/server/common/data/{Firmware.java => OtaPackage.java} (82%) rename common/data/src/main/java/org/thingsboard/server/common/data/{FirmwareInfo.java => OtaPackageInfo.java} (58%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{FirmwareId.java => OtaPackageId.java} (79%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware => ota}/ChecksumAlgorithm.java (93%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareKey.java => ota/OtaPackageKey.java} (88%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareType.java => ota/OtaPackageType.java} (86%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareUpdateStatus.java => ota/OtaPackageUpdateStatus.java} (88%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareUtil.java => ota/OtaPackageUtil.java} (52%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{FirmwareEntity.java => OtaPackageEntity.java} (62%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{FirmwareInfoEntity.java => OtaPackageInfoEntity.java} (63%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java rename dao/src/main/java/org/thingsboard/server/dao/{firmware/FirmwareDao.java => ota/OtaPackageDao.java} (81%) rename dao/src/main/java/org/thingsboard/server/dao/{firmware/FirmwareInfoDao.java => ota/OtaPackageInfoDao.java} (55%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java rename dao/src/main/java/org/thingsboard/server/dao/sql/{firmware/JpaFirmwareDao.java => ota/JpaOtaPackageDao.java} (62%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/{firmware/FirmwareRepository.java => ota/OtaPackageRepository.java} (78%) rename dao/src/test/java/org/thingsboard/server/dao/service/{BaseFirmwareServiceTest.java => BaseOtaPackageServiceTest.java} (74%) rename dao/src/test/java/org/thingsboard/server/dao/service/sql/{FirmwareServiceSqlTest.java => OtaPackageServiceSqlTest.java} (83%) diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 4fec49130a..2814e18c2f 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -59,8 +59,8 @@ CREATE TABLE IF NOT EXISTS resource ( CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid, @@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); ALTER TABLE dashboard @@ -101,13 +101,13 @@ DO $$ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN ALTER TABLE device_profile ADD CONSTRAINT fk_firmware_device_profile - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN ALTER TABLE device_profile ADD CONSTRAINT fk_software_device_profile - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN @@ -119,13 +119,13 @@ DO $$ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN ALTER TABLE device ADD CONSTRAINT fk_firmware_device - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN ALTER TABLE device ADD CONSTRAINT fk_software_device - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; END; $$; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index e6384c010d..dd1d388a08 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -39,8 +39,8 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.TbResourceInfo; @@ -70,7 +70,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -110,7 +110,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; @@ -128,7 +128,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.firmware.FirmwareStateService; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeGrpcService; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; @@ -250,10 +250,10 @@ public abstract class BaseController { protected TbResourceService resourceService; @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; @Autowired - protected FirmwareStateService firmwareStateService; + protected OtaPackageStateService otaPackageStateService; @Autowired protected TbQueueProducerProvider producerProvider; @@ -511,8 +511,8 @@ public abstract class BaseController { case TB_RESOURCE: checkResourceId(new TbResourceId(entityId.getId()), operation); return; - case FIRMWARE: - checkFirmwareId(new FirmwareId(entityId.getId()), operation); + case OTA_PACKAGE: + checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); @@ -769,25 +769,25 @@ public abstract class BaseController { } } - Firmware checkFirmwareId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { + OtaPackage checkOtaPackageId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { try { - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); - Firmware firmware = firmwareService.findFirmwareById(getCurrentUser().getTenantId(), firmwareId); - checkNotNull(firmware); - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmware); - return firmware; + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); + OtaPackage otaPackage = otaPackageService.findOtaPackageById(getCurrentUser().getTenantId(), otaPackageId); + checkNotNull(otaPackage); + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackage); + return otaPackage; } catch (Exception e) { throw handleException(e, false); } } - FirmwareInfo checkFirmwareInfoId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { + OtaPackageInfo checkOtaPackageInfoId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { try { - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); - FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(getCurrentUser().getTenantId(), firmwareId); - checkNotNull(firmwareInfo); - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmwareInfo); - return firmwareInfo; + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); + OtaPackageInfo otaPackageIn = otaPackageService.findOtaPackageInfoById(getCurrentUser().getTenantId(), otaPackageId); + checkNotNull(otaPackageIn); + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackageIn); + return otaPackageIn; } catch (Exception e) { throw handleException(e, false); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 378083b873..90094e88c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -75,6 +76,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; import static org.thingsboard.server.controller.EdgeController.EDGE_ID; @@ -153,7 +155,7 @@ public class DeviceController extends BaseController { deviceStateService.onDeviceUpdated(savedDevice); } - firmwareStateService.update(savedDevice, oldDevice); + otaPackageStateService.update(savedDevice, oldDevice); return savedDevice; } catch (Exception e) { @@ -778,4 +780,19 @@ public class DeviceController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/devices/count", method = RequestMethod.GET) + @ResponseBody + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@RequestParam(required = false) String otaPackageType, + @RequestParam(required = false) String deviceProfileId) throws ThingsboardException { + checkParameter("OtaPackageType", otaPackageType); + checkParameter("DeviceProfileId", deviceProfileId); + try { + return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( + getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(deviceProfileId)); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 849bcb51b9..ceee45147b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -168,7 +168,7 @@ public class DeviceProfileController extends BaseController { null, created ? ActionType.ADDED : ActionType.UPDATED, null); - firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); + otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); diff --git a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java b/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java deleted file mode 100644 index 6728120163..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright © 2016-2021 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.controller; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.security.permission.Resource; - -import java.nio.ByteBuffer; - -@Slf4j -@RestController -@TbCoreComponent -@RequestMapping("/api") -public class FirmwareController extends BaseController { - - public static final String FIRMWARE_ID = "firmwareId"; - public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; - - @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET) - @ResponseBody - public ResponseEntity downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - Firmware firmware = checkFirmwareId(firmwareId, Operation.READ); - - ByteArrayResource resource = new ByteArrayResource(firmware.getData().array()); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName()) - .header("x-filename", firmware.getFileName()) - .contentLength(resource.contentLength()) - .contentType(parseMediaType(firmware.getContentType())) - .body(resource); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) - @ResponseBody - public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET) - @ResponseBody - public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - return checkFirmwareId(firmwareId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware", method = RequestMethod.POST) - @ResponseBody - public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { - boolean created = firmwareInfo.getId() == null; - try { - firmwareInfo.setTenantId(getTenantId()); - checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); - FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); - logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, null); - return savedFirmwareInfo; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, e); - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST) - @ResponseBody - public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId, - @RequestParam(required = false) String checksum, - @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, - @RequestBody MultipartFile file) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ); - - Firmware firmware = new Firmware(firmwareId); - firmware.setCreatedTime(info.getCreatedTime()); - firmware.setTenantId(getTenantId()); - firmware.setDeviceProfileId(info.getDeviceProfileId()); - firmware.setType(info.getType()); - firmware.setTitle(info.getTitle()); - firmware.setVersion(info.getVersion()); - firmware.setAdditionalInfo(info.getAdditionalInfo()); - - ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); - - byte[] bytes = file.getBytes(); - if (StringUtils.isEmpty(checksum)) { - checksum = firmwareService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); - } - - firmware.setChecksumAlgorithm(checksumAlgorithm); - firmware.setChecksum(checksum); - firmware.setFileName(file.getOriginalFilename()); - firmware.setContentType(file.getContentType()); - firmware.setData(ByteBuffer.wrap(bytes)); - firmware.setDataSize((long) bytes.length); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); - logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null); - return savedFirmware; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId); - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmwares", method = RequestMethod.GET) - @ResponseBody - public PageData getFirmwares(@RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) - @ResponseBody - public PageData getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId, - @PathVariable("type") String strType, - @PathVariable("hasData") boolean hasData, - @RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { - checkParameter("deviceProfileId", strDeviceProfileId); - checkParameter("type", strType); - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), - new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) - @ResponseBody - public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE); - firmwareService.deleteFirmware(getTenantId(), firmwareId); - logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId); - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId); - throw handleException(e); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java new file mode 100644 index 0000000000..02e9d4b305 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -0,0 +1,222 @@ +/** + * Copyright © 2016-2021 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.controller; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import java.nio.ByteBuffer; + +@Slf4j +@RestController +@TbCoreComponent +@RequestMapping("/api") +public class OtaPackageController extends BaseController { + + public static final String OTA_PACKAGE_ID = "otaPackageId"; + public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; + + @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadOtaPackage(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); + + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) + .header("x-filename", otaPackage.getFileName()) + .contentLength(resource.contentLength()) + .contentType(parseMediaType(otaPackage.getContentType())) + .body(resource); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET) + @ResponseBody + public OtaPackageInfo getOtaPackageInfoById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET) + @ResponseBody + public OtaPackage getOtaPackageById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkOtaPackageId(otaPackageId, Operation.READ); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) + @ResponseBody + public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException { + boolean created = otaPackageInfo.getId() == null; + try { + otaPackageInfo.setTenantId(getTenantId()); + checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo); + logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, + null, created ? ActionType.ADDED : ActionType.UPDATED, null); + return savedOtaPackageInfo; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo, + null, created ? ActionType.ADDED : ActionType.UPDATED, e); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST) + @ResponseBody + public OtaPackage saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId, + @RequestParam(required = false) String checksum, + @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, + @RequestBody MultipartFile file) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ); + + OtaPackage otaPackage = new OtaPackage(otaPackageId); + otaPackage.setCreatedTime(info.getCreatedTime()); + otaPackage.setTenantId(getTenantId()); + otaPackage.setDeviceProfileId(info.getDeviceProfileId()); + otaPackage.setType(info.getType()); + otaPackage.setTitle(info.getTitle()); + otaPackage.setVersion(info.getVersion()); + otaPackage.setAdditionalInfo(info.getAdditionalInfo()); + + ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); + + byte[] bytes = file.getBytes(); + if (StringUtils.isEmpty(checksum)) { + checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); + } + + otaPackage.setChecksumAlgorithm(checksumAlgorithm); + otaPackage.setChecksum(checksum); + otaPackage.setFileName(file.getOriginalFilename()); + otaPackage.setContentType(file.getContentType()); + otaPackage.setData(ByteBuffer.wrap(bytes)); + otaPackage.setDataSize((long) bytes.length); + OtaPackage savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); + logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null); + return savedOtaPackage; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackages", method = RequestMethod.GET) + @ResponseBody + public PageData getOtaPackages(@RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + try { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) + @ResponseBody + public PageData getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId, + @PathVariable("type") String strType, + @PathVariable("hasData") boolean hasData, + @RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + checkParameter("deviceProfileId", strDeviceProfileId); + checkParameter("type", strType); + try { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), hasData, pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE) + @ResponseBody + public void deleteOtaPackage(@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); + otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId); + logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId); + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId); + throw handleException(e); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java similarity index 69% rename from application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java rename to application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 9720cd2097..c5d0c0472f 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.firmware; +package org.thingsboard.server.service.ota; import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; @@ -23,12 +23,9 @@ import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -37,13 +34,16 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.firmware.FirmwareService; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; @@ -58,44 +58,43 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -import java.util.function.Function; - -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey; + +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTargetTelemetryKey; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTelemetryKey; @Slf4j @Service @TbCoreComponent -public class DefaultFirmwareStateService implements FirmwareStateService { +public class DefaultOtaPackageStateService implements OtaPackageStateService { private final TbClusterService tbClusterService; - private final FirmwareService firmwareService; + private final OtaPackageService otaPackageService; private final DeviceService deviceService; private final DeviceProfileService deviceProfileService; private final RuleEngineTelemetryService telemetryService; - private final TbQueueProducer> fwStateMsgProducer; + private final TbQueueProducer> otaPackageStateMsgProducer; - public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService, - DeviceService deviceService, - DeviceProfileService deviceProfileService, - RuleEngineTelemetryService telemetryService, - TbCoreQueueFactory coreQueueFactory) { + public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService, + DeviceService deviceService, + DeviceProfileService deviceProfileService, + RuleEngineTelemetryService telemetryService, + TbCoreQueueFactory coreQueueFactory) { this.tbClusterService = tbClusterService; - this.firmwareService = firmwareService; + this.otaPackageService = otaPackageService; this.deviceService = deviceService; this.deviceProfileService = deviceProfileService; this.telemetryService = telemetryService; - this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer(); + this.otaPackageStateMsgProducer = coreQueueFactory.createToOtaPackageStateServiceMsgProducer(); } @Override @@ -105,14 +104,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } private void updateFirmware(Device device, Device oldDevice) { - FirmwareId newFirmwareId = device.getFirmwareId(); + OtaPackageId newFirmwareId = device.getFirmwareId(); if (newFirmwareId == null) { DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); newFirmwareId = newDeviceProfile.getFirmwareId(); } if (oldDevice != null) { if (newFirmwareId != null) { - FirmwareId oldFirmwareId = oldDevice.getFirmwareId(); + OtaPackageId oldFirmwareId = oldDevice.getFirmwareId(); if (oldFirmwareId == null) { DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); oldFirmwareId = oldDeviceProfile.getFirmwareId(); @@ -132,14 +131,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } private void updateSoftware(Device device, Device oldDevice) { - FirmwareId newSoftwareId = device.getSoftwareId(); + OtaPackageId newSoftwareId = device.getSoftwareId(); if (newSoftwareId == null) { DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); newSoftwareId = newDeviceProfile.getSoftwareId(); } if (oldDevice != null) { if (newSoftwareId != null) { - FirmwareId oldSoftwareId = oldDevice.getSoftwareId(); + OtaPackageId oldSoftwareId = oldDevice.getSoftwareId(); if (oldSoftwareId == null) { DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); oldSoftwareId = oldDeviceProfile.getSoftwareId(); @@ -170,33 +169,20 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } } - private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) { - Function> getDevicesFunction; + private void update(TenantId tenantId, DeviceProfile deviceProfile, OtaPackageType otaPackageType) { Consumer updateConsumer; - switch (firmwareType) { - case FIRMWARE: - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl); - break; - case SOFTWARE: - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl); - break; - default: - log.warn("Unsupported firmware type: [{}]", firmwareType); - return; - } - if (deviceProfile.getFirmwareId() != null) { long ts = System.currentTimeMillis(); - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType); + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, otaPackageType); } else { - updateConsumer = d -> remove(d, firmwareType); + updateConsumer = d -> remove(d, otaPackageType); } PageLink pageLink = new PageLink(100); PageData pageData; do { - pageData = getDevicesFunction.apply(pageLink); + pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfile.getId(), otaPackageType, pageLink); pageData.getData().forEach(updateConsumer); if (pageData.hasNext()) { @@ -206,60 +192,60 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } @Override - public boolean process(ToFirmwareStateServiceMsg msg) { + public boolean process(ToOtaPackageStateServiceMsg msg) { boolean isSuccess = false; - FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); + OtaPackageId targetOtaPackageId = new OtaPackageId(new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB())); DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); - FirmwareType firmwareType = FirmwareType.valueOf(msg.getType()); + OtaPackageType firmwareType = OtaPackageType.valueOf(msg.getType()); long ts = msg.getTs(); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); } else { - FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); - if (currentFirmwareId == null) { + OtaPackageId currentOtaPackageId = OtaPackageUtil.getOtaPackageId(device, firmwareType); + if (currentOtaPackageId == null) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()); - currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); + currentOtaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, firmwareType); } - if (targetFirmwareId.equals(currentFirmwareId)) { - update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts); + if (targetOtaPackageId.equals(currentOtaPackageId)) { + update(device, otaPackageService.findOtaPackageInfoById(device.getTenantId(), targetOtaPackageId), ts); isSuccess = true; } else { - log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); + log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetOtaPackageId, currentOtaPackageId); } } return isSuccess; } - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) { - ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() + private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, long ts, OtaPackageType firmwareType) { + ToOtaPackageStateServiceMsg msg = ToOtaPackageStateServiceMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) - .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) - .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) + .setOtaPackageIdMSB(firmwareId.getId().getMostSignificantBits()) + .setOtaPackageIdLSB(firmwareId.getId().getLeastSignificantBits()) .setType(firmwareType.name()) .setTs(ts) .build(); - FirmwareInfo firmware = firmwareService.findFirmwareInfoById(tenantId, firmwareId); + OtaPackageInfo firmware = otaPackageService.findOtaPackageInfoById(tenantId, firmwareId); if (firmware == null) { log.warn("[{}] Failed to send firmware update because firmware was already deleted", firmwareId); return; } - TopicPartitionInfo tpi = new TopicPartitionInfo(fwStateMsgProducer.getDefaultTopic(), null, null, false); - fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); + TopicPartitionInfo tpi = new TopicPartitionInfo(otaPackageStateMsgProducer.getDefaultTopic(), null, null, false); + otaPackageStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); List telemetry = new ArrayList<>(); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name()))); + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { @Override @@ -275,11 +261,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } - private void update(Device device, FirmwareInfo firmware, long ts) { + private void update(Device device, OtaPackageInfo firmware, long ts) { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name())); + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.INITIATED.name())); telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { @Override @@ -313,14 +299,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { }); } - private void remove(Device device, FirmwareType firmwareType) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType), + private void remove(Device device, OtaPackageType firmwareType) { + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType), new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success remove target firmware attributes!", device.getId()); Set keysToNotify = new HashSet<>(); - FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); + OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); } diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java similarity index 79% rename from application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java rename to application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java index 8562f096c9..9392d1c888 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.firmware; +package org.thingsboard.server.service.ota; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -public interface FirmwareStateService { +public interface OtaPackageStateService { void update(Device device, Device oldDevice); void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged); - boolean process(ToFirmwareStateServiceMsg msg); + boolean process(ToOtaPackageStateServiceMsg msg); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index b8c8698d52..ceb364f921 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -50,7 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.queue.TbQueueConsumer; @@ -60,7 +60,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; -import org.thingsboard.server.service.firmware.FirmwareStateService; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.processing.IdMsgPair; @@ -75,7 +75,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -101,9 +100,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> mainConsumer; @@ -113,10 +112,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> usageStatsConsumer; - private final TbQueueConsumer> firmwareStatesConsumer; + private final TbQueueConsumer> firmwareStatesConsumer; protected volatile ExecutorService usageStatsExecutor; @@ -135,11 +134,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { while (!stopped) { try { - List> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); + List> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); if (msgs.isEmpty()) { continue; } long timeToSleep = maxProcessingTimeoutPerRecord; - for (TbProtoQueueMsg msg : msgs) { + for (TbProtoQueueMsg msg : msgs) { try { long startTime = System.currentTimeMillis(); - boolean isSuccessUpdate = handleFirmwareUpdates(msg); + boolean isSuccessUpdate = handleOtaPackageUpdates(msg); long endTime = System.currentTimeMillis(); long spentTime = endTime - startTime; timeToSleep = timeToSleep - spentTime; @@ -402,7 +401,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService msg) { + private boolean handleOtaPackageUpdates(TbProtoQueueMsg msg) { return firmwareStateService.process(msg.getValue()); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index 914b9ce7fa..540a47e8b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -46,7 +46,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TbResourceId; @@ -63,7 +63,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -135,7 +135,7 @@ public class AccessValidator { protected ResourceService resourceService; @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; private ExecutorService executor; @@ -232,8 +232,8 @@ public class AccessValidator { case TB_RESOURCE: validateResource(currentUser, operation, entityId, callback); return; - case FIRMWARE: - validateFirmware(currentUser, operation, entityId, callback); + case OTA_PACKAGE: + validateOtaPackage(currentUser, operation, entityId, callback); return; default: //TODO: add support of other entities @@ -300,20 +300,20 @@ public class AccessValidator { } } - private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { + private void validateOtaPackage(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { if (currentUser.isSystemAdmin()) { callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); } else { - FirmwareInfo firmware = firmwareService.findFirmwareInfoById(currentUser.getTenantId(), new FirmwareId(entityId.getId())); - if (firmware == null) { - callback.onSuccess(ValidationResult.entityNotFound("Firmware with requested id wasn't found!")); + OtaPackageInfo otaPackage = otaPackageService.findOtaPackageInfoById(currentUser.getTenantId(), new OtaPackageId(entityId.getId())); + if (otaPackage == null) { + callback.onSuccess(ValidationResult.entityNotFound("OtaPackage with requested id wasn't found!")); } else { try { - accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware); + accessControlService.checkPermission(currentUser, Resource.OTA_PACKAGE, operation, entityId, otaPackage); } catch (ThingsboardException e) { callback.onSuccess(ValidationResult.accessDenied(e.getMessage())); } - callback.onSuccess(ValidationResult.ok(firmware)); + callback.onSuccess(ValidationResult.ok(otaPackage)); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 75119c402d..43c420a94a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -38,7 +38,7 @@ public enum Resource { DEVICE_PROFILE(EntityType.DEVICE_PROFILE), API_USAGE_STATE(EntityType.API_USAGE_STATE), TB_RESOURCE(EntityType.TB_RESOURCE), - FIRMWARE(EntityType.FIRMWARE), + OTA_PACKAGE(EntityType.OTA_PACKAGE), EDGE(EntityType.EDGE); private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index af08e38087..8b4d44e938 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -42,7 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker); put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker); put(Resource.TB_RESOURCE, tbResourcePermissionChecker); - put(Resource.FIRMWARE, tenantEntityPermissionChecker); + put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker); put(Resource.EDGE, tenantEntityPermissionChecker); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index bd2df29617..76bbe1f518 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -26,27 +26,27 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -64,7 +64,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; @@ -124,8 +124,8 @@ public class DefaultTransportApiService implements TransportApiService { private final DataDecodingEncodingService dataDecodingEncodingService; private final DeviceProvisionService deviceProvisionService; private final TbResourceService resourceService; - private final FirmwareService firmwareService; - private final FirmwareDataCache firmwareDataCache; + private final OtaPackageService otaPackageService; + private final OtaPackageDataCache otaPackageDataCache; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -134,7 +134,7 @@ public class DefaultTransportApiService implements TransportApiService { RelationService relationService, DeviceCredentialsService deviceCredentialsService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, - DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) { + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, OtaPackageService otaPackageService, OtaPackageDataCache otaPackageDataCache) { this.deviceProfileCache = deviceProfileCache; this.tenantProfileCache = tenantProfileCache; this.apiUsageStateService = apiUsageStateService; @@ -147,8 +147,8 @@ public class DefaultTransportApiService implements TransportApiService { this.dataDecodingEncodingService = dataDecodingEncodingService; this.deviceProvisionService = deviceProvisionService; this.resourceService = resourceService; - this.firmwareService = firmwareService; - this.firmwareDataCache = firmwareDataCache; + this.otaPackageService = otaPackageService; + this.otaPackageDataCache = otaPackageDataCache; } @Override @@ -184,8 +184,8 @@ public class DefaultTransportApiService implements TransportApiService { result = handle(transportApiRequestMsg.getDeviceRequestMsg()); } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) { result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); - } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) { - result = handle(transportApiRequestMsg.getFirmwareRequestMsg()); + } else if (transportApiRequestMsg.hasOtaPackageRequestMsg()) { + result = handle(transportApiRequestMsg.getOtaPackageRequestMsg()); } return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), @@ -511,50 +511,50 @@ public class DefaultTransportApiService implements TransportApiService { } } - private ListenableFuture handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { + private ListenableFuture handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) { TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); - FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType()); + OtaPackageType otaPackageType = OtaPackageType.valueOf(requestMsg.getType()); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { return getEmptyTransportApiResponseFuture(); } - FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); - if (firmwareId == null) { + OtaPackageId otaPackageId = OtaPackageUtil.getOtaPackageId(device, otaPackageType); + if (otaPackageId == null) { DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId()); - firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); + otaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, otaPackageType); } - TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); + TransportProtos.GetOtaPackageResponseMsg.Builder builder = TransportProtos.GetOtaPackageResponseMsg.newBuilder(); - if (firmwareId == null) { + if (otaPackageId == null) { builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); } else { - FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(tenantId, firmwareId); + OtaPackageInfo otaPackageInfo = otaPackageService.findOtaPackageInfoById(tenantId, otaPackageId); - if (firmwareInfo == null) { + if (otaPackageInfo == null) { builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); } else { builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); - builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); - builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); - builder.setType(firmwareInfo.getType().name()); - builder.setTitle(firmwareInfo.getTitle()); - builder.setVersion(firmwareInfo.getVersion()); - builder.setFileName(firmwareInfo.getFileName()); - builder.setContentType(firmwareInfo.getContentType()); - if (!firmwareDataCache.has(firmwareId.toString())) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId); - firmwareDataCache.put(firmwareId.toString(), firmware.getData().array()); + builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits()); + builder.setOtaPackageIdLSB(otaPackageId.getId().getLeastSignificantBits()); + builder.setType(otaPackageInfo.getType().name()); + builder.setTitle(otaPackageInfo.getTitle()); + builder.setVersion(otaPackageInfo.getVersion()); + builder.setFileName(otaPackageInfo.getFileName()); + builder.setContentType(otaPackageInfo.getContentType()); + if (!otaPackageDataCache.has(otaPackageId.toString())) { + OtaPackage otaPackage = otaPackageService.findOtaPackageById(tenantId, otaPackageId); + otaPackageDataCache.put(otaPackageId.toString(), otaPackage.getData().array()); } } } return Futures.immediateFuture( TransportApiResponseMsg.newBuilder() - .setFirmwareResponseMsg(builder.build()) + .setOtaPackageResponseMsg(builder.build()) .build()); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 7fde122cf6..ae628bf9f6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -371,7 +371,7 @@ caffeine: tokensOutdatageTime: timeToLiveInMinutes: 20000 maxSize: 10000 - firmwares: + otaPackages: timeToLiveInMinutes: 60 maxSize: 10 edges: @@ -497,7 +497,7 @@ audit-log: "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" "edge": "${AUDIT_LOG_MASK_EDGE:W}" "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}" - "firmware": "${AUDIT_LOG_MASK_FIRMWARE:W}" + "ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}" sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" @@ -749,7 +749,7 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" consumer-properties-per-topic: - tb_firmware: + tb_ota_package: - key: max.poll.records value: 10 other: @@ -759,7 +759,7 @@ queue: transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" - fw-updates: "${TB_QUEUE_KAFKA_FW_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" consumer-stats: enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" @@ -830,10 +830,10 @@ queue: poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" - firmware: - topic: "${TB_QUEUE_CORE_FW_TOPIC:tb_firmware}" - pack-interval-ms: "${TB_QUEUE_CORE_FW_PACK_INTERVAL_MS:60000}" - pack-size: "${TB_QUEUE_CORE_FW_PACK_SIZE:100}" + ota: + topic: "${TB_QUEUE_CORE_OTA_TOPIC:tb_ota_package}" + pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" + pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java similarity index 66% rename from application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java rename to application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java index ec36be13a5..9b73053831 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -25,11 +25,10 @@ import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -41,11 +40,11 @@ import java.util.Collections; import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { +public abstract class BaseOtaPackageControllerTest extends AbstractControllerTest { - private IdComparator idComparator = new IdComparator<>(); + private IdComparator idComparator = new IdComparator<>(); public static final String TITLE = "My firmware"; private static final String FILE_NAME = "filename.txt"; @@ -93,13 +92,13 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testSaveFirmware() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -112,19 +111,19 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest save(savedFirmwareInfo); - FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); } @Test public void testSaveFirmwareData() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -137,12 +136,12 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest save(savedFirmwareInfo); - FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); Assert.assertEquals(FILE_NAME, savedFirmware.getFileName()); Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType()); @@ -150,97 +149,97 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testUpdateFirmwareFromDifferentTenant() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); loginDifferentTenant(); - doPost("/api/firmware", savedFirmwareInfo, FirmwareInfo.class, status().isForbidden()); + doPost("/api/otaPackage", savedFirmwareInfo, OtaPackageInfo.class, status().isForbidden()); deleteDifferentTenant(); } @Test public void testFindFirmwareInfoById() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); - FirmwareInfo foundFirmware = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmware = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmwareInfo, foundFirmware); } @Test public void testFindFirmwareById() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - Firmware foundFirmware = doGet("/api/firmware/" + savedFirmwareInfo.getId().getId().toString(), Firmware.class); + OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); } @Test public void testDeleteFirmware() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); - doDelete("/api/firmware/" + savedFirmwareInfo.getId().getId().toString()) + doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString()) + doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString()) .andExpect(status().isNotFound()); } @Test public void testFindTenantFirmwares() throws Exception { - List firmwares = new ArrayList<>(); + List otaPackages = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); if (i > 100) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - firmwares.add(new FirmwareInfo(savedFirmware)); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + otaPackages.add(new OtaPackageInfo(savedFirmware)); } else { - firmwares.add(savedFirmwareInfo); + otaPackages.add(savedFirmwareInfo); } } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(24); - PageData pageData; + PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/firmwares?", + pageData = doGetTypedWithPageLink("/api/otaPackages?", new TypeReference<>() { }, pageLink); loadedFirmwares.addAll(pageData.getData()); @@ -249,41 +248,41 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - Collections.sort(firmwares, idComparator); + Collections.sort(otaPackages, idComparator); Collections.sort(loadedFirmwares, idComparator); - Assert.assertEquals(firmwares, loadedFirmwares); + Assert.assertEquals(otaPackages, loadedFirmwares); } @Test public void testFindTenantFirmwaresByHasData() throws Exception { - List firmwaresWithData = new ArrayList<>(); - List firmwaresWithoutData = new ArrayList<>(); + List otaPackagesWithData = new ArrayList<>(); + List otaPackagesWithoutData = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); if (i > 100) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - firmwaresWithData.add(new FirmwareInfo(savedFirmware)); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + otaPackagesWithData.add(new OtaPackageInfo(savedFirmware)); } else { - firmwaresWithoutData.add(savedFirmwareInfo); + otaPackagesWithoutData.add(savedFirmwareInfo); } } - List loadedFirmwaresWithData = new ArrayList<>(); + List loadedFirmwaresWithData = new ArrayList<>(); PageLink pageLink = new PageLink(24); - PageData pageData; + PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?", + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/true?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithData.addAll(pageData.getData()); @@ -292,10 +291,10 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - List loadedFirmwaresWithoutData = new ArrayList<>(); + List loadedFirmwaresWithoutData = new ArrayList<>(); pageLink = new PageLink(24); do { - pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?", + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/false?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithoutData.addAll(pageData.getData()); @@ -304,25 +303,25 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - Collections.sort(firmwaresWithData, idComparator); - Collections.sort(firmwaresWithoutData, idComparator); + Collections.sort(otaPackagesWithData, idComparator); + Collections.sort(otaPackagesWithoutData, idComparator); Collections.sort(loadedFirmwaresWithData, idComparator); Collections.sort(loadedFirmwaresWithoutData, idComparator); - Assert.assertEquals(firmwaresWithData, loadedFirmwaresWithData); - Assert.assertEquals(firmwaresWithoutData, loadedFirmwaresWithoutData); + Assert.assertEquals(otaPackagesWithData, loadedFirmwaresWithData); + Assert.assertEquals(otaPackagesWithoutData, loadedFirmwaresWithoutData); } - private FirmwareInfo save(FirmwareInfo firmwareInfo) throws Exception { - return doPost("/api/firmware", firmwareInfo, FirmwareInfo.class); + private OtaPackageInfo save(OtaPackageInfo firmwareInfo) throws Exception { + return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); } - protected Firmware savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { + protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); postRequest.file(content); setJwtToken(postRequest); - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), Firmware.class); + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java rename to application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java index a0e4a838ca..92bd9ba9c6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.controller.sql; -import org.thingsboard.server.controller.BaseFirmwareControllerTest; +import org.thingsboard.server.controller.BaseOtaPackageControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest -public class FirmwareControllerSqlTest extends BaseFirmwareControllerTest { +public class OtaPackageControllerSqlTest extends BaseOtaPackageControllerTest { } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java similarity index 84% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java index 1acb09b28e..a864fc6dba 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @RequiredArgsConstructor -public class CaffeineFirmwareCache implements FirmwareDataCache { +public class CaffeineOtaPackageCache implements OtaPackageDataCache { private final CacheManager cacheManager; @@ -36,7 +36,7 @@ public class CaffeineFirmwareCache implements FirmwareDataCache { @Override public byte[] get(String key, int chunkSize, int chunk) { - byte[] data = cacheManager.getCache(FIRMWARE_CACHE).get(key, byte[].class); + byte[] data = cacheManager.getCache(OTA_PACKAGE_CACHE).get(key, byte[].class); if (chunkSize < 1) { return data; @@ -58,11 +58,11 @@ public class CaffeineFirmwareCache implements FirmwareDataCache { @Override public void put(String key, byte[] value) { - cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); + cacheManager.getCache(OTA_PACKAGE_CACHE).putIfAbsent(key, value); } @Override public void evict(String key) { - cacheManager.getCache(FIRMWARE_CACHE).evict(key); + cacheManager.getCache(OTA_PACKAGE_CACHE).evict(key); } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java similarity index 82% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java index 99f2f0d521..77057406c2 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; -public interface FirmwareDataCache { +public interface OtaPackageDataCache { byte[] get(String key); @@ -25,8 +25,8 @@ public interface FirmwareDataCache { void evict(String key); - default boolean has(String firmwareId) { - byte[] data = get(firmwareId, 1, 0); + default boolean has(String otaPackageId) { + byte[] data = get(otaPackageId, 1, 0); return data != null && data.length > 0; } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java similarity index 78% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java index 08dd6facc2..1e4ae53829 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -21,12 +21,12 @@ import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.stereotype.Service; -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @RequiredArgsConstructor -public class RedisFirmwareDataCache implements FirmwareDataCache { +public class RedisOtaPackageDataCache implements OtaPackageDataCache { private final RedisConnectionFactory redisConnectionFactory; @@ -39,30 +39,30 @@ public class RedisFirmwareDataCache implements FirmwareDataCache { public byte[] get(String key, int chunkSize, int chunk) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { if (chunkSize == 0) { - return connection.get(toFirmwareCacheKey(key)); + return connection.get(toOtaPackageCacheKey(key)); } int startIndex = chunkSize * chunk; int endIndex = startIndex + chunkSize - 1; - return connection.getRange(toFirmwareCacheKey(key), startIndex, endIndex); + return connection.getRange(toOtaPackageCacheKey(key), startIndex, endIndex); } } @Override public void put(String key, byte[] value) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { - connection.set(toFirmwareCacheKey(key), value); + connection.set(toOtaPackageCacheKey(key), value); } } @Override public void evict(String key) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { - connection.del(toFirmwareCacheKey(key)); + connection.del(toOtaPackageCacheKey(key)); } } - private byte[] toFirmwareCacheKey(String key) { - return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); + private byte[] toOtaPackageCacheKey(String key) { + return String.format("%s::%s", OTA_PACKAGE_CACHE, key).getBytes(); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index b4692ec5bf..761211f32c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -63,9 +64,9 @@ public interface DeviceService { PageData findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink); + Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType); PageData findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java deleted file mode 100644 index eeaafbd777..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.firmware; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; - -import java.nio.ByteBuffer; - -public interface FirmwareService { - - FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo); - - Firmware saveFirmware(Firmware firmware); - - String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data); - - Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId); - - FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId); - - ListenableFuture findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId); - - PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); - - PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); - - void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); - - void deleteFirmwaresByTenantId(TenantId tenantId); -} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java new file mode 100644 index 0000000000..589bdf14b6 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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.dao.ota; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +import java.nio.ByteBuffer; + +public interface OtaPackageService { + + OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo); + + OtaPackage saveOtaPackage(OtaPackage otaPackage); + + String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data); + + OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId); + + OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId); + + ListenableFuture findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId); + + PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink); + + PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); + + void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId); + + void deleteOtaPackagesByTenantId(TenantId tenantId); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index ce4576705b..3cc3f56737 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -29,5 +29,5 @@ public class CacheConstants { public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; public static final String ATTRIBUTES_CACHE = "attributes"; public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime"; - public static final String FIRMWARE_CACHE = "firmwares"; + public static final String OTA_PACKAGE_CACHE = "otaPackages"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index bce3ba703f..9abc619b48 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.NoXss; @@ -32,7 +32,7 @@ import java.io.IOException; @EqualsAndHashCode(callSuper = true) @Slf4j -public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasFirmware { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasOtaPackage { private static final long serialVersionUID = 2807343040519543363L; @@ -49,8 +49,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen @JsonIgnore private byte[] deviceDataBytes; - private FirmwareId firmwareId; - private FirmwareId softwareId; + private OtaPackageId firmwareId; + private OtaPackageId softwareId; public Device() { super(); @@ -167,19 +167,19 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen return getName(); } - public FirmwareId getFirmwareId() { + public OtaPackageId getFirmwareId() { return firmwareId; } - public void setFirmwareId(FirmwareId firmwareId) { + public void setFirmwareId(OtaPackageId firmwareId) { this.firmwareId = firmwareId; } - public FirmwareId getSoftwareId() { + public OtaPackageId getSoftwareId() { return softwareId; } - public void setSoftwareId(FirmwareId softwareId) { + public void setSoftwareId(OtaPackageId softwareId) { this.softwareId = softwareId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index e35fbb84a0..13c4692976 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -23,7 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.NoXss; @@ -37,7 +37,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @Data @EqualsAndHashCode(callSuper = true) @Slf4j -public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasFirmware { +public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasOtaPackage { private TenantId tenantId; @NoXss @@ -60,9 +60,9 @@ public class DeviceProfile extends SearchTextBased implements H @NoXss private String provisionDeviceKey; - private FirmwareId firmwareId; + private OtaPackageId firmwareId; - private FirmwareId softwareId; + private OtaPackageId softwareId; public DeviceProfile() { super(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index d3508d42dd..cf6c6fd9a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, FIRMWARE, EDGE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java similarity index 80% rename from common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java rename to common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java index ae05829092..09320e13d0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; -public interface HasFirmware { +public interface HasOtaPackage { - FirmwareId getFirmwareId(); + OtaPackageId getFirmwareId(); - FirmwareId getSoftwareId(); + OtaPackageId getSoftwareId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java similarity index 82% rename from common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java rename to common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java index b4155a578a..6110310cd3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java @@ -17,27 +17,27 @@ package org.thingsboard.server.common.data; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import java.nio.ByteBuffer; @Data @EqualsAndHashCode(callSuper = true) -public class Firmware extends FirmwareInfo { +public class OtaPackage extends OtaPackageInfo { private static final long serialVersionUID = 3091601761339422546L; private transient ByteBuffer data; - public Firmware() { + public OtaPackage() { super(); } - public Firmware(FirmwareId id) { + public OtaPackage(OtaPackageId id) { super(id); } - public Firmware(Firmware firmware) { + public OtaPackage(OtaPackage firmware) { super(firmware); this.data = firmware.getData(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java similarity index 58% rename from common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java index 33b529e303..5a33a95215 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java @@ -19,22 +19,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; @Slf4j @Data @EqualsAndHashCode(callSuper = true) -public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { +public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { private static final long serialVersionUID = 3168391583570815419L; private TenantId tenantId; private DeviceProfileId deviceProfileId; - private FirmwareType type; + private OtaPackageType type; private String title; private String version; private boolean hasData; @@ -45,27 +45,27 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo private Long dataSize; - public FirmwareInfo() { + public OtaPackageInfo() { super(); } - public FirmwareInfo(FirmwareId id) { + public OtaPackageInfo(OtaPackageId id) { super(id); } - public FirmwareInfo(FirmwareInfo firmwareInfo) { - super(firmwareInfo); - this.tenantId = firmwareInfo.getTenantId(); - this.deviceProfileId = firmwareInfo.getDeviceProfileId(); - this.type = firmwareInfo.getType(); - this.title = firmwareInfo.getTitle(); - this.version = firmwareInfo.getVersion(); - this.hasData = firmwareInfo.isHasData(); - this.fileName = firmwareInfo.getFileName(); - this.contentType = firmwareInfo.getContentType(); - this.checksumAlgorithm = firmwareInfo.getChecksumAlgorithm(); - this.checksum = firmwareInfo.getChecksum(); - this.dataSize = firmwareInfo.getDataSize(); + public OtaPackageInfo(OtaPackageInfo otaPackageInfo) { + super(otaPackageInfo); + this.tenantId = otaPackageInfo.getTenantId(); + this.deviceProfileId = otaPackageInfo.getDeviceProfileId(); + this.type = otaPackageInfo.getType(); + this.title = otaPackageInfo.getTitle(); + this.version = otaPackageInfo.getVersion(); + this.hasData = otaPackageInfo.isHasData(); + this.fileName = otaPackageInfo.getFileName(); + this.contentType = otaPackageInfo.getContentType(); + this.checksumAlgorithm = otaPackageInfo.getChecksumAlgorithm(); + this.checksum = otaPackageInfo.getChecksum(); + this.dataSize = otaPackageInfo.getDataSize(); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index df2846eead..a4b2327c75 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -71,8 +71,8 @@ public class EntityIdFactory { return new ApiUsageStateId(uuid); case TB_RESOURCE: return new TbResourceId(uuid); - case FIRMWARE: - return new FirmwareId(uuid); + case OTA_PACKAGE: + return new OtaPackageId(uuid); case EDGE: return new EdgeId(uuid); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java index 3cdee53f58..0442792238 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java @@ -22,23 +22,23 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class FirmwareId extends UUIDBased implements EntityId { +public class OtaPackageId extends UUIDBased implements EntityId { private static final long serialVersionUID = 1L; @JsonCreator - public FirmwareId(@JsonProperty("id") UUID id) { + public OtaPackageId(@JsonProperty("id") UUID id) { super(id); } - public static FirmwareId fromString(String firmwareId) { - return new FirmwareId(UUID.fromString(firmwareId)); + public static OtaPackageId fromString(String firmwareId) { + return new OtaPackageId(UUID.fromString(firmwareId)); } @JsonIgnore @Override public EntityType getEntityType() { - return EntityType.FIRMWARE; + return EntityType.OTA_PACKAGE; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java similarity index 93% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java index 3998482e35..ce33463054 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; public enum ChecksumAlgorithm { MD5, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java index cb38b6724c..0528b9dfe3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.Getter; -public enum FirmwareKey { +public enum OtaPackageKey { TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"); @Getter private final String value; - FirmwareKey(String value) { + OtaPackageKey(String value) { this.value = value; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java similarity index 86% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java index 5f6aa0d925..cab8cf1847 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.Getter; -public enum FirmwareType { +public enum OtaPackageType { FIRMWARE("fw"), SOFTWARE("sw"); @Getter private final String keyPrefix; - FirmwareType(String keyPrefix) { + OtaPackageType(String keyPrefix) { this.keyPrefix = keyPrefix; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java index 3e46174792..caa043c595 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; -public enum FirmwareUpdateStatus { +public enum OtaPackageUpdateStatus { QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java similarity index 52% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java index 646ad24173..f2b29ec1f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java @@ -13,21 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.HasFirmware; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.HasOtaPackage; +import org.thingsboard.server.common.data.id.OtaPackageId; import java.util.ArrayList; import java.util.Collections; import java.util.List; - -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; +import java.util.function.Supplier; @Slf4j -public class FirmwareUtil { +public class OtaPackageUtil { public static final List ALL_FW_ATTRIBUTE_KEYS; @@ -35,19 +33,19 @@ public class FirmwareUtil { static { ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>(); - for (FirmwareKey key : FirmwareKey.values()) { - ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key)); + for (OtaPackageKey key : OtaPackageKey.values()) { + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.FIRMWARE, key)); } ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>(); - for (FirmwareKey key : FirmwareKey.values()) { - ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key)); + for (OtaPackageKey key : OtaPackageKey.values()) { + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.SOFTWARE, key)); } } - public static List getAttributeKeys(FirmwareType firmwareType) { + public static List getAttributeKeys(OtaPackageType firmwareType) { switch (firmwareType) { case FIRMWARE: return ALL_FW_ATTRIBUTE_KEYS; @@ -57,35 +55,46 @@ public class FirmwareUtil { return Collections.emptyList(); } - public static String getAttributeKey(FirmwareType type, FirmwareKey key) { + public static String getAttributeKey(OtaPackageType type, OtaPackageKey key) { return type.getKeyPrefix() + "_" + key.getValue(); } - public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getTargetTelemetryKey(OtaPackageType type, OtaPackageKey key) { return getTelemetryKey("target_", type, key); } - public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getCurrentTelemetryKey(OtaPackageType type, OtaPackageKey key) { return getTelemetryKey("current_", type, key); } - private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) { + private static String getTelemetryKey(String prefix, OtaPackageType type, OtaPackageKey key) { return prefix + type.getKeyPrefix() + "_" + key.getValue(); } - public static String getTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getTelemetryKey(OtaPackageType type, OtaPackageKey key) { return type.getKeyPrefix() + "_" + key.getValue(); } - public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) { - switch (firmwareType) { + public static OtaPackageId getOtaPackageId(HasOtaPackage entity, OtaPackageType type) { + switch (type) { case FIRMWARE: return entity.getFirmwareId(); case SOFTWARE: return entity.getSoftwareId(); default: - log.warn("Unsupported firmware type: [{}]", firmwareType); + log.warn("Unsupported ota package type: [{}]", type); return null; } } + + public static T getByOtaPackageType(Supplier firmwareSupplier, Supplier softwareSupplier, OtaPackageType type) { + switch (type) { + case FIRMWARE: + return firmwareSupplier.get(); + case SOFTWARE: + return softwareSupplier.get(); + default: + throw new RuntimeException("Unsupported OtaPackage type: " + type); + } + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java index 1a6a1a56e4..624918c8dd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java @@ -38,7 +38,7 @@ public class TbKafkaTopicConfigs { private String notificationsProperties; @Value("${queue.kafka.topic-properties.js-executor}") private String jsExecutorProperties; - @Value("${queue.kafka.topic-properties.fw-updates:}") + @Value("${queue.kafka.topic-properties.ota-updates:}") private String fwUpdatesProperties; @Getter diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java index c2f4f4f750..d89b03c744 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java @@ -187,14 +187,14 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java index 0267cdecb8..eaa73bc108 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -177,14 +177,14 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 8806176f9f..cd1c34b612 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -131,13 +131,13 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new InMemoryTbQueueConsumer<>(coreSettings.getFirmwareTopic()); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new InMemoryTbQueueConsumer<>(coreSettings.getOtaPackageTopic()); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new InMemoryTbQueueProducer<>(coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new InMemoryTbQueueProducer<>(coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index a526331116..c57260bef8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -277,24 +277,24 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getFirmwareTopic()); - consumerBuilder.clientId("monolith-fw-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-fw-consumer"); - consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.clientId("monolith-ota-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId("monolith-ota-consumer"); + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); - requestBuilder.clientId("monolith-fw-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getFirmwareTopic()); + requestBuilder.clientId("monolith-ota-producer-" + serviceInfoProvider.getServiceId()); + requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 5f64e42ffd..bec01c7201 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -245,24 +245,24 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getFirmwareTopic()); - consumerBuilder.clientId("tb-core-fw-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-fw-consumer"); - consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.clientId("tb-core-ota-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId("tb-core-ota-consumer"); + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); - requestBuilder.clientId("tb-core-fw-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getFirmwareTopic()); + requestBuilder.clientId("tb-core-ota-producer-" + serviceInfoProvider.getServiceId()); + requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index 3504c6aef2..3f014f578c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -22,9 +22,9 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; -import org.thingsboard.server.gen.transport.TransportProtos.*; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -192,14 +192,14 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index a9ebf5a4de..65ea183d66 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -166,14 +166,14 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java index cd6043cdde..7730f8f5dd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java @@ -24,7 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -190,14 +190,14 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java index 8b5ad00de4..c0abeab162 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -172,14 +172,14 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java index b569d13742..2443093cf0 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -189,14 +189,14 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java index 17e6eb9a27..7a9da236ed 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -172,14 +172,14 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index be5ca967bf..584a6e389b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -16,7 +16,7 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; @@ -91,14 +91,14 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory { * * @return */ - TbQueueConsumer> createToFirmwareStateServiceMsgConsumer(); + TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer(); /** * Used to consume messages about firmware update notifications by TB Core Service * * @return */ - TbQueueProducer> createToFirmwareStateServiceMsgProducer(); + TbQueueProducer> createToOtaPackageStateServiceMsgProducer(); /** * Used to consume high priority messages by TB Core Service diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java index b7e7a155ed..6c8ab4455b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.queue.provider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; @@ -25,11 +24,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; import javax.annotation.PostConstruct; @Service -@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") +@TbCoreComponent public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { private final TbCoreQueueFactory tbQueueProvider; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java index d194fdb4ea..3c9ff5813e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java @@ -26,8 +26,8 @@ public class TbQueueCoreSettings { @Value("${queue.core.topic}") private String topic; - @Value("${queue.core.firmware.topic:tb_firmware}") - private String firmwareTopic; + @Value("${queue.core.ota.topic:tb_ota_package}") + private String otaPackageTopic; @Value("${queue.core.usage-stats-topic:tb_usage_stats}") private String usageStatsTopic; diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index b4d929f60c..faf88c5620 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -388,7 +388,7 @@ enum ResponseStatus { FAILURE = 3; } -message GetFirmwareRequestMsg { +message GetOtaPackageRequestMsg { int64 deviceIdMSB = 1; int64 deviceIdLSB = 2; int64 tenantIdMSB = 3; @@ -396,10 +396,10 @@ message GetFirmwareRequestMsg { string type = 5; } -message GetFirmwareResponseMsg { +message GetOtaPackageResponseMsg { ResponseStatus responseStatus = 1; - int64 firmwareIdMSB = 2; - int64 firmwareIdLSB = 3; + int64 otaPackageIdMSB = 2; + int64 otaPackageIdLSB = 3; string type = 4; string title = 5; string version = 6; @@ -627,7 +627,7 @@ message TransportApiRequestMsg { ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; GetResourceRequestMsg resourceRequestMsg = 9; - GetFirmwareRequestMsg firmwareRequestMsg = 10; + GetOtaPackageRequestMsg otaPackageRequestMsg = 10; GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; GetDeviceRequestMsg deviceRequestMsg = 12; GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; @@ -642,7 +642,7 @@ message TransportApiResponseMsg { GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; LwM2MResponseMsg lwM2MResponseMsg = 6; GetResourceResponseMsg resourceResponseMsg = 7; - GetFirmwareResponseMsg firmwareResponseMsg = 8; + GetOtaPackageResponseMsg otaPackageResponseMsg = 8; GetDeviceResponseMsg deviceResponseMsg = 9; GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; } @@ -710,13 +710,13 @@ message ToUsageStatsServiceMsg { int64 customerIdLSB = 7; } -message ToFirmwareStateServiceMsg { +message ToOtaPackageStateServiceMsg { int64 ts = 1; int64 tenantIdMSB = 2; int64 tenantIdLSB = 3; int64 deviceIdMSB = 4; int64 deviceIdLSB = 5; - int64 firmwareIdMSB = 6; - int64 firmwareIdLSB = 7; + int64 otaPackageIdMSB = 6; + int64 otaPackageIdLSB = 7; string type = 8; } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 47cf44f6fa..cd80aa42df 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -44,7 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -350,10 +350,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { new CoapNoOpCallback(exchange)); break; case GET_FIRMWARE_REQUEST: - getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE); + getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.FIRMWARE); break; case GET_SOFTWARE_REQUEST: - getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE); + getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.SOFTWARE); break; } } catch (AdaptorException e) { @@ -366,14 +366,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); } - private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) { - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); + transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); } private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { @@ -470,25 +470,25 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private class FirmwareCallback implements TransportServiceCallback { + private class OtaPackageCallback implements TransportServiceCallback { private final CoapExchange exchange; - FirmwareCallback(CoapExchange exchange) { + OtaPackageCallback(CoapExchange exchange) { this.exchange = exchange; } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) { String title = exchange.getQueryParameter("title"); String version = exchange.getQueryParameter("version"); if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { - String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString(); + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); String strChunkSize = exchange.getQueryParameter("size"); String strChunk = exchange.getQueryParameter("chunk"); int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); - exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk)); + exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); } else { exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 31180b478d..f4a3f705af 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -34,7 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.SessionMsgListener; @@ -213,7 +213,7 @@ public class DeviceApiController implements TbTransportService { @RequestParam(value = "version") String version, @RequestParam(value = "size", required = false, defaultValue = "0") int size, @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { - return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE); + return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.FIRMWARE); } @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) @@ -222,7 +222,7 @@ public class DeviceApiController implements TbTransportService { @RequestParam(value = "version") String version, @RequestParam(value = "size", required = false, defaultValue = "0") int size, @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { - return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE); + return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.SOFTWARE); } @RequestMapping(value = "/provision", method = RequestMethod.POST) @@ -233,17 +233,17 @@ public class DeviceApiController implements TbTransportService { return responseWriter; } - private DeferredResult getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) { + private DeferredResult getOtaPackageCallback(String deviceToken, String title, String version, int size, int chunk, OtaPackageType firmwareType) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk)); + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(responseWriter, title, version, size, chunk)); })); return responseWriter; } @@ -294,14 +294,14 @@ public class DeviceApiController implements TbTransportService { } } - private class GetFirmwareCallback implements TransportServiceCallback { + private class GetOtaPackageCallback implements TransportServiceCallback { private final DeferredResult responseWriter; private final String title; private final String version; private final int chuckSize; private final int chuck; - GetFirmwareCallback(DeferredResult responseWriter, String title, String version, int chuckSize, int chuck) { + GetOtaPackageCallback(DeferredResult responseWriter, String title, String version, int chuckSize, int chuck) { this.responseWriter = responseWriter; this.title = title; this.version = version; @@ -310,17 +310,17 @@ public class DeviceApiController implements TbTransportService { } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg) { - if (!TransportProtos.ResponseStatus.SUCCESS.equals(firmwareResponseMsg.getResponseStatus())) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) { + if (!TransportProtos.ResponseStatus.SUCCESS.equals(otaPackageResponseMsg.getResponseStatus())) { responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); - } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) { - String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString(); - ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareDataCache().get(firmwareId, chuckSize, chuck)); + } else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) { + String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString(); + ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck)); ResponseEntity response = ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName()) - .header("x-filename", firmwareResponseMsg.getFileName()) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName()) + .header("x-filename", otaPackageResponseMsg.getFileName()) .contentLength(resource.contentLength()) - .contentType(parseMediaType(firmwareResponseMsg.getContentType())) + .contentType(parseMediaType(otaPackageResponseMsg.getContentType())) .body(resource); responseWriter.setResult(response); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 9092f8abeb..abe2a50855 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -39,13 +39,13 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.firmware.FirmwareKey; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -87,8 +87,8 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED; @@ -132,7 +132,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler private final TransportService transportService; private final LwM2mTransportContext context; public final LwM2MTransportServerConfig config; - public final FirmwareDataCache firmwareDataCache; + public final OtaPackageDataCache otaPackageDataCache; public final LwM2mTransportServerHelper helper; private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; @@ -143,14 +143,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @Lazy LwM2mTransportRequest lwM2mTransportRequest, - FirmwareDataCache firmwareDataCache, + OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.config = config; this.helper = helper; this.clientContext = clientContext; this.lwM2mTransportRequest = lwM2mTransportRequest; - this.firmwareDataCache = firmwareDataCache; + this.otaPackageDataCache = otaPackageDataCache; this.context = context; this.adaptor = adaptor; this.rpcSubscriptions = new ConcurrentHashMap<>(); @@ -357,14 +357,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) + if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) - || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) + || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { this.getInfoFirmwareUpdate(lwM2MClient); - } else if ((FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName) + } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) - || (FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName) + || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { this.getInfoSoftwareUpdate(lwM2MClient); } @@ -391,7 +391,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { + if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); } }); @@ -1344,18 +1344,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { - transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()), + transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), new TransportServiceCallback<>() { @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(FirmwareType.FIRMWARE.name())) { + && response.getType().equals(OtaPackageType.FIRMWARE.name())) { lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); + lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } else { - log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); + log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); } } @@ -1373,15 +1373,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { DefaultLwM2MTransportMsgHandler serviceImpl = this; - transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.SOFTWARE.name()), + transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), new TransportServiceCallback<>() { @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(FirmwareType.SOFTWARE.name())) { + && response.getType().equals(OtaPackageType.SOFTWARE.name())) { lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); + lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); @@ -1397,8 +1397,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - private TransportProtos.GetFirmwareRequestMsg createFirmwareRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { - return TransportProtos.GetFirmwareRequestMsg.newBuilder() + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB()) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index e5cd289758..87e72c2873 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -71,8 +71,8 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 320059b0a7..a013f75c1b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -43,10 +43,10 @@ import org.eclipse.leshan.server.registration.Registration; import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareKey; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -77,12 +77,12 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADING; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.VERIFIED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; @@ -139,7 +139,7 @@ public class LwM2mTransportUtil { public static final String ERROR_KEY = "error"; public static final String METHOD_KEY = "methodName"; - // FirmWare + // Firmware public static final String FW_UPDATE = "Firmware update"; public static final Integer FW_ID = 5; // Package W @@ -155,7 +155,7 @@ public class LwM2mTransportUtil { // Update E public static final String FW_UPDATE_ID = "/5/0/2"; - // SoftWare + // Software public static final String SW_UPDATE = "Software update"; public static final Integer SW_ID = 9; // Package W @@ -354,7 +354,7 @@ public class LwM2mTransportUtil { * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static FirmwareUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { + public static OtaPackageUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { switch (updateResultFw) { case INITIAL: switch (stateFw) { @@ -500,7 +500,7 @@ public class LwM2mTransportUtil { * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static FirmwareUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { + public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { switch (updateResultSw) { case INITIAL: switch (updateStateSw) { @@ -932,15 +932,15 @@ public class LwM2mTransportUtil { } public static boolean isFwSwWords (String pathName) { - return FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.CHECKSUM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.CHECKSUM_ALGORITHM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.SIZE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.CHECKSUM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.CHECKSUM_ALGORITHM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.SIZE).equals(pathName); + return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 9ef45c3b3b..a6a1fadbf3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -31,7 +31,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; @@ -120,8 +120,8 @@ public class LwM2mClient implements Cloneable { this.init = false; this.queuedRequests = new ConcurrentLinkedQueue<>(); - this.fwUpdate = new LwM2mFwSwUpdate(this, FirmwareType.FIRMWARE); - this.swUpdate = new LwM2mFwSwUpdate(this, FirmwareType.SOFTWARE); + this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE); + this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE); if (this.credentials != null && this.credentials.hasDeviceInfo()) { this.session = createSession(nodeId, sessionId, credentials); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index e3e8608839..a0e5432c5a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -20,8 +20,8 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.request.ContentFormat; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest; @@ -32,11 +32,11 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; @@ -97,14 +97,14 @@ public class LwM2mFwSwUpdate { @Getter @Setter private volatile boolean infoFwSwUpdate = false; - private final FirmwareType type; + private final OtaPackageType type; @Getter LwM2mClient lwM2MClient; @Getter @Setter private final List pendingInfoRequestsStart; - public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, FirmwareType type) { + public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) { this.lwM2MClient = lwM2MClient; this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.type = type; @@ -139,7 +139,7 @@ public class LwM2mFwSwUpdate { } if (this.pendingInfoRequestsStart.size() == 0) { this.infoFwSwUpdate = false; - if (!FirmwareUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { + if (!OtaPackageUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() : this.conditionalSwUpdateStart(); if (conditionalStart) { @@ -154,12 +154,12 @@ public class LwM2mFwSwUpdate { * before operation Write: fw_state = DOWNLOADING */ private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { - this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name(); + this.stateUpdate = OtaPackageUpdateStatus.DOWNLOADING.name(); // this.observeStateUpdate(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); int chunkSize = 0; int chunk = 0; - byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk); + byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), firmwareChunk, handler.config.getTimeout(), null); @@ -287,10 +287,10 @@ public class LwM2mFwSwUpdate { LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { - this.stateUpdate = FirmwareUpdateStatus.UPDATED.name(); + this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); } else { - this.stateUpdate = FirmwareUpdateStatus.FAILED.name(); + this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value); } handler.helper.sendParametersOnThingsboardTelemetry( diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index f2a6922b6d..961c20ed34 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -47,8 +47,8 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.transport.SessionMsgListener; @@ -126,8 +126,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private volatile InetSocketAddress address; private volatile GatewaySessionHandler gatewaySessionHandler; - private final ConcurrentHashMap fwSessions; - private final ConcurrentHashMap fwChunkSizes; + private final ConcurrentHashMap otaPackSessions; + private final ConcurrentHashMap chunkSizes; MqttTransportHandler(MqttTransportContext context, SslHandler sslHandler) { this.sessionId = UUID.randomUUID(); @@ -137,8 +137,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement this.sslHandler = sslHandler; this.mqttQoSMap = new ConcurrentHashMap<>(); this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context); - this.fwSessions = new ConcurrentHashMap<>(); - this.fwChunkSizes = new ConcurrentHashMap<>(); + this.otaPackSessions = new ConcurrentHashMap<>(); + this.chunkSizes = new ConcurrentHashMap<>(); } @Override @@ -320,9 +320,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) { - getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE); + getOtaPackageCallback(ctx, mqttMsg, msgId, fwMatcher, OtaPackageType.FIRMWARE); } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) { - getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE); + getOtaPackageCallback(ctx, mqttMsg, msgId, fwMatcher, OtaPackageType.SOFTWARE); } else { transportService.reportActivity(deviceSessionCtx.getSessionInfo()); ack(ctx, msgId); @@ -334,38 +334,38 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) { + private void getOtaPackageCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, OtaPackageType type) { String payload = mqttMsg.content().toString(UTF8); int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; String requestId = fwMatcher.group("requestId"); int chunk = Integer.parseInt(fwMatcher.group("chunk")); if (chunkSize > 0) { - this.fwChunkSizes.put(requestId, chunkSize); + this.chunkSizes.put(requestId, chunkSize); } else { - chunkSize = fwChunkSizes.getOrDefault(requestId, 0); + chunkSize = chunkSizes.getOrDefault(requestId, 0); } if (chunkSize > context.getMaxPayloadSize()) { - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); + sendOtaPackageError(ctx, PAYLOAD_TOO_LARGE); return; } - String firmwareId = fwSessions.get(requestId); + String otaPackageId = otaPackSessions.get(requestId); - if (firmwareId != null) { - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type); + if (otaPackageId != null) { + sendOtaPackage(ctx, mqttMsg.variableHeader().packetId(), otaPackageId, requestId, chunkSize, chunk, type); } else { TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + TransportProtos.GetOtaPackageRequestMsg getOtaPackageRequestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setType(type.name()) .build(); - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, - new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk)); + transportService.process(deviceSessionCtx.getSessionInfo(), getOtaPackageRequestMsg, + new OtaPackageCallback(ctx, msgId, getOtaPackageRequestMsg, requestId, chunkSize, chunk)); } } @@ -425,15 +425,15 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private class FirmwareCallback implements TransportServiceCallback { + private class OtaPackageCallback implements TransportServiceCallback { private final ChannelHandlerContext ctx; private final int msgId; - private final TransportProtos.GetFirmwareRequestMsg msg; + private final TransportProtos.GetOtaPackageRequestMsg msg; private final String requestId; private final int chunkSize; private final int chunk; - FirmwareCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.GetFirmwareRequestMsg msg, String requestId, int chunkSize, int chunk) { + OtaPackageCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.GetOtaPackageRequestMsg msg, String requestId, int chunkSize, int chunk) { this.ctx = ctx; this.msgId = msgId; this.msg = msg; @@ -443,13 +443,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { - FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); - fwSessions.put(requestId, firmwareId.toString()); - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType())); + OtaPackageId firmwareId = new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); + otaPackSessions.put(requestId, firmwareId.toString()); + sendOtaPackage(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, OtaPackageType.valueOf(response.getType())); } else { - sendFirmwareError(ctx, response.getResponseStatus().toString()); + sendOtaPackageError(ctx, response.getResponseStatus().toString()); } } @@ -460,11 +460,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) { + private void sendOtaPackage(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) { log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); ack(ctx, msgId); try { - byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); + byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk); deviceSessionCtx.getPayloadAdaptor() .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type) .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); @@ -473,7 +473,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void sendFirmwareError(ChannelHandlerContext ctx, String error) { + private void sendOtaPackageError(ChannelHandlerContext ctx, String error) { log.warn("[{}] {}", sessionId, error); deviceSessionCtx.getChannel().writeAndFlush(deviceSessionCtx .getPayloadAdaptor() diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index a9d7b3e2ea..19d2e15fc5 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; @@ -155,7 +155,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) { + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) { return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index d0c1f30524..62ae6ae027 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -23,7 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; @@ -78,7 +78,7 @@ public interface MqttTransportAdaptor { Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; - Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { MqttFixedHeader mqttFixedHeader = diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java index 08a2f9abe3..a007c73a5a 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java @@ -28,7 +28,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.ProtoConverter; @@ -168,7 +168,7 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException { + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException { return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index d4798108c6..d48d3fd04e 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -21,14 +21,13 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * Created by ashvayka on 15.10.18. @@ -53,7 +52,7 @@ public abstract class TransportContext { @Getter @Autowired - private FirmwareDataCache firmwareDataCache; + private OtaPackageDataCache otaPackageDataCache; @Autowired private TransportResourceCache transportResourceCache; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index 2209ffc305..4a4e68f64f 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -29,8 +29,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg; @@ -115,7 +115,7 @@ public interface TransportService { void process(TransportToDeviceActorMsg msg, TransportServiceCallback callback); - void process(SessionInfoProto sessionInfoProto, GetFirmwareRequestMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfoProto, GetOtaPackageRequestMsg msg, TransportServiceCallback callback); SessionMetaData registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 7035c3374e..77b75dc07a 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -614,13 +614,13 @@ public class DefaultTransportService implements TransportService { } @Override - public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetFirmwareRequestMsg msg, TransportServiceCallback callback) { + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetOtaPackageRequestMsg msg, TransportServiceCallback callback) { if (checkLimits(sessionInfo, msg, callback)) { TbProtoQueueMsg protoMsg = - new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setFirmwareRequestMsg(msg).build()); + new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setOtaPackageRequestMsg(msg).build()); AsyncCallbackTemplate.withCallback(transportApiRequestTemplate.send(protoMsg), response -> { - callback.onSuccess(response.getValue().getFirmwareResponseMsg()); + callback.onSuccess(response.getValue().getOtaPackageResponseMsg()); }, callback::onError, transportCallbackExecutor); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 34982b92b6..de13c85727 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; @@ -81,9 +82,12 @@ public interface DeviceDao extends Dao, TenantEntityDao { */ PageData findDevicesByTenantIdAndType(UUID tenantId, String type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(UUID tenantId, + UUID deviceProfileId, + OtaPackageType type, + PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink); + Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(UUID tenantId, UUID deviceProfileId, OtaPackageType otaPackageType); /** * Find device infos by tenantId, type and page link. diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 8158134925..e41289e97b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -43,7 +43,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; @@ -57,7 +57,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -66,7 +66,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -119,7 +119,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D private CacheManager cacheManager; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Autowired private RuleChainService ruleChainService; @@ -427,11 +427,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } if (deviceProfile.getFirmwareId() != null) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId()); + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } - if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); } if (firmware.getData() == null) { @@ -443,11 +443,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } if (deviceProfile.getSoftwareId() != null) { - Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId()); + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); if (software == null) { throw new DataValidationException("Can't assign non-existent software!"); } - if (!software.getType().equals(FirmwareType.SOFTWARE)) { + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { throw new DataValidationException("Can't assign software with type: " + software.getType()); } if (software.getData() == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 237d53a7f2..34aa462a6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; @@ -54,13 +54,13 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -76,7 +76,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -138,7 +138,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe private TbTenantProfileCache tenantProfileCache; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { @@ -201,14 +201,12 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe deviceCredentials.setDeviceId(savedDevice.getId()); if (device.getId() == null) { deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); - } - else { + } else { DeviceCredentials foundDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), savedDevice.getId()); if (foundDeviceCredentials == null) { deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); - } - else { - deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); + } else { + deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); } } return savedDevice; @@ -364,21 +362,24 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + public PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, + DeviceProfileId deviceProfileId, + OtaPackageType type, + PageLink pageLink) { + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}], pageLink [{}]", + tenantId, deviceProfileId, type, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); validatePageLink(pageLink); - return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink); + return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink); } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type) { + log.trace("Executing countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}]", tenantId, deviceProfileId, type); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); - validatePageLink(pageLink); - return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink); + validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); + return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type); } @Override @@ -708,11 +709,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe .ifPresent(DeviceTransportConfiguration::validate); if (device.getFirmwareId() != null) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, device.getFirmwareId()); + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } - if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); } if (firmware.getData() == null) { @@ -724,11 +725,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } if (device.getSoftwareId() != null) { - Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId()); + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); if (software == null) { throw new DataValidationException("Can't assign non-existent software!"); } - if (!software.getType().equals(FirmwareType.SOFTWARE)) { + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { throw new DataValidationException("Can't assign software with type: " + software.getType()); } if (software.getData() == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 73cebc0797..7686d9ec21 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; @@ -49,7 +49,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -102,7 +102,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private ResourceService resourceService; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public void deleteEntityRelations(TenantId tenantId, EntityId entityId) { @@ -167,8 +167,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case TB_RESOURCE: hasName = resourceService.findResourceInfoByIdAsync(tenantId, new TbResourceId(entityId.getId())); break; - case FIRMWARE: - hasName = firmwareService.findFirmwareInfoByIdAsync(tenantId, new FirmwareId(entityId.getId())); + case OTA_PACKAGE: + hasName = otaPackageService.findOtaPackageInfoByIdAsync(tenantId, new OtaPackageId(entityId.getId())); break; default: throw new IllegalStateException("Not Implemented!"); @@ -192,7 +192,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case DEVICE_PROFILE: case API_USAGE_STATE: case TB_RESOURCE: - case FIRMWARE: + case OTA_PACKAGE: break; case CUSTOMER: hasCustomerId = () -> new CustomerId(entityId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java deleted file mode 100644 index 2e0d5c03b6..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.firmware; - -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; -import com.google.common.util.concurrent.ListenableFuture; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.hibernate.exception.ConstraintViolationException; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.device.DeviceProfileDao; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TenantDao; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validatePageLink; - -@Service -@Slf4j -@RequiredArgsConstructor -public class BaseFirmwareService implements FirmwareService { - public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId "; - public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - - private final TenantDao tenantDao; - private final DeviceProfileDao deviceProfileDao; - private final FirmwareDao firmwareDao; - private final FirmwareInfoDao firmwareInfoDao; - private final CacheManager cacheManager; - private final FirmwareDataCache firmwareDataCache; - - @Override - public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) { - log.trace("Executing saveFirmwareInfo [{}]", firmwareInfo); - firmwareInfoValidator.validate(firmwareInfo, FirmwareInfo::getTenantId); - try { - FirmwareId firmwareId = firmwareInfo.getId(); - if (firmwareId != null) { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - } - return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { - throw new DataValidationException("Firmware with such title and version already exists!"); - } else { - throw t; - } - } - } - - @Override - public Firmware saveFirmware(Firmware firmware) { - log.trace("Executing saveFirmware [{}]", firmware); - firmwareValidator.validate(firmware, FirmwareInfo::getTenantId); - try { - FirmwareId firmwareId = firmware.getId(); - if (firmwareId != null) { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - } - return firmwareDao.save(firmware.getTenantId(), firmware); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { - throw new DataValidationException("Firmware with such title and version already exists!"); - } else { - throw t; - } - } - } - - @Override - public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) { - if (data == null || !data.hasArray() || data.array().length == 0) { - throw new DataValidationException("Firmware data should be specified!"); - } - - return getHashFunction(checksumAlgorithm).hashBytes(data.array()).toString(); - } - - private HashFunction getHashFunction(ChecksumAlgorithm checksumAlgorithm) { - switch (checksumAlgorithm) { - case MD5: - return Hashing.md5(); - case SHA256: - return Hashing.sha256(); - case SHA384: - return Hashing.sha384(); - case SHA512: - return Hashing.sha512(); - case CRC32: - return Hashing.crc32(); - case MURMUR3_32: - return Hashing.murmur3_32(); - case MURMUR3_128: - return Hashing.murmur3_128(); - default: - throw new DataValidationException("Unknown checksum algorithm!"); - } - } - - @Override - public Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareById [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareDao.findById(tenantId, firmwareId.getId()); - } - - @Override - @Cacheable(cacheNames = FIRMWARE_CACHE, key = "{#firmwareId}") - public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareInfoById [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareInfoDao.findById(tenantId, firmwareId.getId()); - } - - @Override - public ListenableFuture findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); - } - - @Override - public PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findTenantFirmwaresByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validatePageLink(pageLink); - return firmwareInfoDao.findFirmwareInfoByTenantId(tenantId, pageLink); - } - - @Override - public PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { - log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validatePageLink(pageLink); - return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink); - } - - @Override - public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing deleteFirmware [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - try { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - firmwareDao.removeById(tenantId, firmwareId.getId()); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) { - throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { - throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { - throw new DataValidationException("The software referenced by the devices cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { - throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); - } else { - throw t; - } - } - } - - @Override - public void deleteFirmwaresByTenantId(TenantId tenantId) { - log.trace("Executing deleteFirmwaresByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - tenantFirmwareRemover.removeEntities(tenantId, tenantId); - } - - private DataValidator firmwareInfoValidator = new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) { - validateImpl(firmwareInfo); - } - - @Override - protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { - FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); - - validateUpdateDeviceProfile(firmware, firmwareOld); - BaseFirmwareService.validateUpdate(firmware, firmwareOld); - } - }; - - private DataValidator firmwareValidator = new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, Firmware firmware) { - validateImpl(firmware); - - if (StringUtils.isEmpty(firmware.getFileName())) { - throw new DataValidationException("Firmware file name should be specified!"); - } - - if (StringUtils.isEmpty(firmware.getContentType())) { - throw new DataValidationException("Firmware content type should be specified!"); - } - - if (firmware.getChecksumAlgorithm() == null) { - throw new DataValidationException("Firmware checksum algorithm should be specified!"); - } - if (StringUtils.isEmpty(firmware.getChecksum())) { - throw new DataValidationException("Firmware checksum should be specified!"); - } - - String currentChecksum; - - currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData()); - - if (!currentChecksum.equals(firmware.getChecksum())) { - throw new DataValidationException("Wrong firmware file!"); - } - } - - @Override - protected void validateUpdate(TenantId tenantId, Firmware firmware) { - Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); - - validateUpdateDeviceProfile(firmware, firmwareOld); - BaseFirmwareService.validateUpdate(firmware, firmwareOld); - - if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { - throw new DataValidationException("Updating firmware data is prohibited!"); - } - } - }; - - private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) { - if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) { - if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) { - throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!"); - } - } - } - - private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { - if (!firmwareOld.getType().equals(firmware.getType())) { - throw new DataValidationException("Updating type is prohibited!"); - } - - if (!firmwareOld.getTitle().equals(firmware.getTitle())) { - throw new DataValidationException("Updating firmware title is prohibited!"); - } - - if (!firmwareOld.getVersion().equals(firmware.getVersion())) { - throw new DataValidationException("Updating firmware version is prohibited!"); - } - - if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) { - throw new DataValidationException("Updating firmware file name is prohibited!"); - } - - if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getChecksum() != null && !firmwareOld.getChecksum().equals(firmware.getChecksum())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getDataSize() != null && !firmwareOld.getDataSize().equals(firmware.getDataSize())) { - throw new DataValidationException("Updating firmware data size is prohibited!"); - } - } - - private void validateImpl(FirmwareInfo firmwareInfo) { - if (firmwareInfo.getTenantId() == null) { - throw new DataValidationException("Firmware should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); - } - } - - if (firmwareInfo.getDeviceProfileId() != null) { - DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId()); - if (deviceProfile == null) { - throw new DataValidationException("Firmware is referencing to non-existent device profile!"); - } - } - - if (firmwareInfo.getType() == null) { - throw new DataValidationException("Type should be specified!"); - } - - if (StringUtils.isEmpty(firmwareInfo.getTitle())) { - throw new DataValidationException("Firmware title should be specified!"); - } - - if (StringUtils.isEmpty(firmwareInfo.getVersion())) { - throw new DataValidationException("Firmware version should be specified!"); - } - } - - private PaginatedRemover tenantFirmwareRemover = - new PaginatedRemover<>() { - - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return firmwareInfoDao.findFirmwareInfoByTenantId(id, pageLink); - } - - @Override - protected void removeEntity(TenantId tenantId, FirmwareInfo entity) { - deleteFirmware(tenantId, entity.getId()); - } - }; - - protected Optional extractConstraintViolationException(Exception t) { - if (t instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) t); - } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) (t.getCause())); - } else { - return Optional.empty(); - } - } - - private static List toFirmwareInfoKey(FirmwareId firmwareId) { - return Collections.singletonList(firmwareId); - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 0bc346e36c..34878a3810 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -479,22 +479,21 @@ public class ModelConstants { public static final String RESOURCE_DATA_COLUMN = "data"; /** - * Firmware constants. - */ - public static final String FIRMWARE_TABLE_NAME = "firmware"; - public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; - public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; - public static final String FIRMWARE_TYPE_COLUMN = "type"; - public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; - public static final String FIRMWARE_VERSION_COLUMN = "version"; - public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; - public static final String FIRMWARE_CONTENT_TYPE_COLUMN = "content_type"; - public static final String FIRMWARE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm"; - public static final String FIRMWARE_CHECKSUM_COLUMN = "checksum"; - public static final String FIRMWARE_DATA_COLUMN = "data"; - public static final String FIRMWARE_DATA_SIZE_COLUMN = "data_size"; - public static final String FIRMWARE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; - public static final String FIRMWARE_HAS_DATA_PROPERTY = "has_data"; + * Ota Package constants. + */ + public static final String OTA_PACKAGE_TABLE_NAME = "ota_package"; + public static final String OTA_PACKAGE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; + public static final String OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; + public static final String OTA_PACKAGE_TYPE_COLUMN = "type"; + public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY; + public static final String OTA_PACKAGE_VERSION_COLUMN = "version"; + public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name"; + public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type"; + public static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm"; + public static final String OTA_PACKAGE_CHECKSUM_COLUMN = "checksum"; + public static final String OTA_PACKAGE_DATA_COLUMN = "data"; + public static final String OTA_PACKAGE_DATA_SIZE_COLUMN = "data_size"; + public static final String OTA_PACKAGE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; /** * Edge constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index ed6a871fea..c9913565e4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -154,10 +154,10 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti device.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); } if (firmwareId != null) { - device.setFirmwareId(new FirmwareId(firmwareId)); + device.setFirmwareId(new OtaPackageId(firmwareId)); } if (softwareId != null) { - device.setSoftwareId(new FirmwareId(softwareId)); + device.setSoftwareId(new OtaPackageId(softwareId)); } device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); device.setName(name); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index ec440526a4..a3d6c491c0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -178,11 +178,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl deviceProfile.setProvisionDeviceKey(provisionDeviceKey); if (firmwareId != null) { - deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); + deviceProfile.setFirmwareId(new OtaPackageId(firmwareId)); } if (softwareId != null) { - deviceProfile.setSoftwareId(new FirmwareId(softwareId)); + deviceProfile.setSoftwareId(new OtaPackageId(softwareId)); } return deviceProfile; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java similarity index 62% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java index e16d0417e4..97e4dbebbd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java @@ -20,11 +20,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -40,75 +40,75 @@ import javax.persistence.Table; import java.nio.ByteBuffer; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CONTENT_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = FIRMWARE_TABLE_NAME) -public class FirmwareEntity extends BaseSqlEntity implements SearchTextEntity { +@Table(name = OTA_PACKAGE_TABLE_NAME) +public class OtaPackageEntity extends BaseSqlEntity implements SearchTextEntity { - @Column(name = FIRMWARE_TENANT_ID_COLUMN) + @Column(name = OTA_PACKAGE_TENANT_ID_COLUMN) private UUID tenantId; - @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + @Column(name = OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN) private UUID deviceProfileId; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_TYPE_COLUMN) - private FirmwareType type; + @Column(name = OTA_PACKAGE_TYPE_COLUMN) + private OtaPackageType type; - @Column(name = FIRMWARE_TITLE_COLUMN) + @Column(name = OTA_PACKAGE_TILE_COLUMN) private String title; - @Column(name = FIRMWARE_VERSION_COLUMN) + @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; - @Column(name = FIRMWARE_FILE_NAME_COLUMN) + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; - @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN) + @Column(name = OTA_PACKAGE_CONTENT_TYPE_COLUMN) private String contentType; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN) private ChecksumAlgorithm checksumAlgorithm; - @Column(name = FIRMWARE_CHECKSUM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_COLUMN) private String checksum; @Lob - @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY") + @Column(name = OTA_PACKAGE_DATA_COLUMN, columnDefinition = "BINARY") private byte[] data; - @Column(name = FIRMWARE_DATA_SIZE_COLUMN) + @Column(name = OTA_PACKAGE_DATA_SIZE_COLUMN) private Long dataSize; @Type(type = "json") - @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) + @Column(name = ModelConstants.OTA_PACKAGE_ADDITIONAL_INFO_COLUMN) private JsonNode additionalInfo; @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; - public FirmwareEntity() { + public OtaPackageEntity() { super(); } - public FirmwareEntity(Firmware firmware) { + public OtaPackageEntity(OtaPackage firmware) { this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); @@ -138,8 +138,8 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex } @Override - public Firmware toData() { - Firmware firmware = new Firmware(new FirmwareId(id)); + public OtaPackage toData() { + OtaPackage firmware = new OtaPackage(new OtaPackageId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); if (deviceProfileId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java similarity index 63% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java index 93a6e83f25..30441ed098 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java @@ -21,11 +21,11 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -40,60 +40,60 @@ import javax.persistence.Table; import javax.persistence.Transient; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CONTENT_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = FIRMWARE_TABLE_NAME) -public class FirmwareInfoEntity extends BaseSqlEntity implements SearchTextEntity { +@Table(name = OTA_PACKAGE_TABLE_NAME) +public class OtaPackageInfoEntity extends BaseSqlEntity implements SearchTextEntity { - @Column(name = FIRMWARE_TENANT_ID_COLUMN) + @Column(name = OTA_PACKAGE_TENANT_ID_COLUMN) private UUID tenantId; - @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + @Column(name = OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN) private UUID deviceProfileId; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_TYPE_COLUMN) - private FirmwareType type; + @Column(name = OTA_PACKAGE_TYPE_COLUMN) + private OtaPackageType type; - @Column(name = FIRMWARE_TITLE_COLUMN) + @Column(name = OTA_PACKAGE_TILE_COLUMN) private String title; - @Column(name = FIRMWARE_VERSION_COLUMN) + @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; - @Column(name = FIRMWARE_FILE_NAME_COLUMN) + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; - @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN) + @Column(name = OTA_PACKAGE_CONTENT_TYPE_COLUMN) private String contentType; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN) private ChecksumAlgorithm checksumAlgorithm; - @Column(name = FIRMWARE_CHECKSUM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_COLUMN) private String checksum; - @Column(name = FIRMWARE_DATA_SIZE_COLUMN) + @Column(name = OTA_PACKAGE_DATA_SIZE_COLUMN) private Long dataSize; @Type(type = "json") - @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) + @Column(name = ModelConstants.OTA_PACKAGE_ADDITIONAL_INFO_COLUMN) private JsonNode additionalInfo; @Column(name = SEARCH_TEXT_PROPERTY) @@ -102,11 +102,11 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S @Transient private boolean hasData; - public FirmwareInfoEntity() { + public OtaPackageInfoEntity() { super(); } - public FirmwareInfoEntity(FirmwareInfo firmware) { + public OtaPackageInfoEntity(OtaPackageInfo firmware) { this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); @@ -124,9 +124,9 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S this.additionalInfo = firmware.getAdditionalInfo(); } - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version, - String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, - Object additionalInfo, boolean hasData) { + public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, + String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, + Object additionalInfo, boolean hasData) { this.id = id; this.createdTime = createdTime; this.tenantId = tenantId; @@ -154,8 +154,8 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S } @Override - public FirmwareInfo toData() { - FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); + public OtaPackageInfo toData() { + OtaPackageInfo firmware = new OtaPackageInfo(new OtaPackageId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); if (deviceProfileId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java new file mode 100644 index 0000000000..ae19576861 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -0,0 +1,373 @@ +/** + * Copyright © 2016-2021 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.dao.ota; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; + +@Service +@Slf4j +@RequiredArgsConstructor +public class BaseOtaPackageService implements OtaPackageService { + public static final String INCORRECT_OTA_PACKAGE_ID = "Incorrect otaPackageId "; + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + + private final TenantDao tenantDao; + private final DeviceProfileDao deviceProfileDao; + private final OtaPackageDao otaPackageDao; + private final OtaPackageInfoDao otaPackageInfoDao; + private final CacheManager cacheManager; + private final OtaPackageDataCache otaPackageDataCache; + + @Override + public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { + log.trace("Executing saveOtaPackageInfo [{}]", otaPackageInfo); + otaPackageInfoValidator.validate(otaPackageInfo, OtaPackageInfo::getTenantId); + try { + OtaPackageId otaPackageId = otaPackageInfo.getId(); + if (otaPackageId != null) { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + } + return otaPackageInfoDao.save(otaPackageInfo.getTenantId(), otaPackageInfo); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("ota_package_tenant_title_version_unq_key")) { + throw new DataValidationException("OtaPackage with such title and version already exists!"); + } else { + throw t; + } + } + } + + @Override + public OtaPackage saveOtaPackage(OtaPackage otaPackage) { + log.trace("Executing saveOtaPackage [{}]", otaPackage); + otaPackageValidator.validate(otaPackage, OtaPackageInfo::getTenantId); + try { + OtaPackageId otaPackageId = otaPackage.getId(); + if (otaPackageId != null) { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + } + return otaPackageDao.save(otaPackage.getTenantId(), otaPackage); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("ota_package_tenant_title_version_unq_key")) { + throw new DataValidationException("OtaPackage with such title and version already exists!"); + } else { + throw t; + } + } + } + + @Override + public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) { + if (data == null || !data.hasArray() || data.array().length == 0) { + throw new DataValidationException("OtaPackage data should be specified!"); + } + + return getHashFunction(checksumAlgorithm).hashBytes(data.array()).toString(); + } + + private HashFunction getHashFunction(ChecksumAlgorithm checksumAlgorithm) { + switch (checksumAlgorithm) { + case MD5: + return Hashing.md5(); + case SHA256: + return Hashing.sha256(); + case SHA384: + return Hashing.sha384(); + case SHA512: + return Hashing.sha512(); + case CRC32: + return Hashing.crc32(); + case MURMUR3_32: + return Hashing.murmur3_32(); + case MURMUR3_128: + return Hashing.murmur3_128(); + default: + throw new DataValidationException("Unknown checksum algorithm!"); + } + } + + @Override + public OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageById [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageDao.findById(tenantId, otaPackageId.getId()); + } + + @Override + @Cacheable(cacheNames = OTA_PACKAGE_CACHE, key = "{#otaPackageId}") + public OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageInfoById [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageInfoDao.findById(tenantId, otaPackageId.getId()); + } + + @Override + public ListenableFuture findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageInfoByIdAsync [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageInfoDao.findByIdAsync(tenantId, otaPackageId.getId()); + } + + @Override + public PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findTenantOtaPackagesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return otaPackageInfoDao.findOtaPackageInfoByTenantId(tenantId, pageLink); + } + + @Override + public PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { + log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, hasData, pageLink); + } + + @Override + public void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing deleteOtaPackage [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + try { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + otaPackageDao.removeById(tenantId, otaPackageId.getId()); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) { + throw new DataValidationException("The otaPackage referenced by the devices cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { + throw new DataValidationException("The otaPackage referenced by the device profile cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { + throw new DataValidationException("The software referenced by the devices cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { + throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); + } else { + throw t; + } + } + } + + @Override + public void deleteOtaPackagesByTenantId(TenantId tenantId) { + log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + tenantOtaPackageRemover.removeEntities(tenantId, tenantId); + } + + private DataValidator otaPackageInfoValidator = new DataValidator<>() { + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackageInfo otaPackageInfo) { + validateImpl(otaPackageInfo); + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { + OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); + + validateUpdateDeviceProfile(otaPackage, otaPackageOld); + BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); + } + }; + + private DataValidator otaPackageValidator = new DataValidator<>() { + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { + validateImpl(otaPackage); + + if (StringUtils.isEmpty(otaPackage.getFileName())) { + throw new DataValidationException("OtaPackage file name should be specified!"); + } + + if (StringUtils.isEmpty(otaPackage.getContentType())) { + throw new DataValidationException("OtaPackage content type should be specified!"); + } + + if (otaPackage.getChecksumAlgorithm() == null) { + throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); + } + if (StringUtils.isEmpty(otaPackage.getChecksum())) { + throw new DataValidationException("OtaPackage checksum should be specified!"); + } + + String currentChecksum; + + currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); + + if (!currentChecksum.equals(otaPackage.getChecksum())) { + throw new DataValidationException("Wrong otaPackage file!"); + } + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { + OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); + + validateUpdateDeviceProfile(otaPackage, otaPackageOld); + BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); + + if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { + throw new DataValidationException("Updating otaPackage data is prohibited!"); + } + } + }; + + private void validateUpdateDeviceProfile(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { + if (otaPackageOld.getDeviceProfileId() != null && !otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { + if (otaPackageInfoDao.isOtaPackageUsed(otaPackageOld.getId(), otaPackage.getType(), otaPackageOld.getDeviceProfileId())) { + throw new DataValidationException("Can`t update deviceProfileId because otaPackage is already in use!"); + } + } + } + + private static void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { + if (!otaPackageOld.getType().equals(otaPackage.getType())) { + throw new DataValidationException("Updating type is prohibited!"); + } + + if (!otaPackageOld.getTitle().equals(otaPackage.getTitle())) { + throw new DataValidationException("Updating otaPackage title is prohibited!"); + } + + if (!otaPackageOld.getVersion().equals(otaPackage.getVersion())) { + throw new DataValidationException("Updating otaPackage version is prohibited!"); + } + + if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { + throw new DataValidationException("Updating otaPackage file name is prohibited!"); + } + + if (otaPackageOld.getContentType() != null && !otaPackageOld.getContentType().equals(otaPackage.getContentType())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksumAlgorithm() != null && !otaPackageOld.getChecksumAlgorithm().equals(otaPackage.getChecksumAlgorithm())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksum() != null && !otaPackageOld.getChecksum().equals(otaPackage.getChecksum())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getDataSize() != null && !otaPackageOld.getDataSize().equals(otaPackage.getDataSize())) { + throw new DataValidationException("Updating otaPackage data size is prohibited!"); + } + } + + private void validateImpl(OtaPackageInfo otaPackageInfo) { + if (otaPackageInfo.getTenantId() == null) { + throw new DataValidationException("OtaPackage should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); + } + } + + if (otaPackageInfo.getDeviceProfileId() != null) { + DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); + if (deviceProfile == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); + } + } + + if (otaPackageInfo.getType() == null) { + throw new DataValidationException("Type should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { + throw new DataValidationException("OtaPackage title should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { + throw new DataValidationException("OtaPackage version should be specified!"); + } + } + + private PaginatedRemover tenantOtaPackageRemover = + new PaginatedRemover<>() { + + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return otaPackageInfoDao.findOtaPackageInfoByTenantId(id, pageLink); + } + + @Override + protected void removeEntity(TenantId tenantId, OtaPackageInfo entity) { + deleteOtaPackage(tenantId, entity.getId()); + } + }; + + protected Optional extractConstraintViolationException(Exception t) { + if (t instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) t); + } else if (t.getCause() instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) (t.getCause())); + } else { + return Optional.empty(); + } + } + + private static List toOtaPackageInfoKey(OtaPackageId otaPackageId) { + return Collections.singletonList(otaPackageId); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java rename to dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index 0cacb47ea0..42f66663d1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.firmware; +package org.thingsboard.server.dao.ota; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.dao.Dao; -public interface FirmwareDao extends Dao { +public interface OtaPackageDao extends Dao { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java index 7cb6c3a57f..d3294f0ec3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java @@ -13,25 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.firmware; +package org.thingsboard.server.dao.ota; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; -import java.util.UUID; +public interface OtaPackageInfoDao extends Dao { -public interface FirmwareInfoDao extends Dao { + PageData findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink); - PageData findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); + PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); - PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); - - boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId); + boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 3bbe18f2ab..58c05547b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -96,23 +96,35 @@ public interface DeviceRepository extends PagingAndSortingRepository findByTenantIdAndTypeAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, - @Param("type") String type, + @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.type = :type " + + "AND d.deviceProfileId = :deviceProfileId " + "AND d.softwareId = null " + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") Page findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, - @Param("type") String type, + @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, Pageable pageable); + @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + + "AND d.deviceProfileId = :deviceProfileId " + + "AND d.firmwareId = null") + Long countByTenantIdAndDeviceProfileIdAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId); + + @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + + "AND d.deviceProfileId = :deviceProfileId " + + "AND d.softwareId = null") + Long countByTenantIdAndDeviceProfileIdAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId); + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + "LEFT JOIN CustomerEntity c on c.id = d.customerId " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 344286d7ac..0e3e843405 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.sql.device; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -27,6 +29,8 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; @@ -155,23 +159,27 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull( - tenantId, - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(UUID tenantId, + UUID deviceProfileId, + OtaPackageType type, + PageLink pageLink) { + Pageable pageable = DaoUtil.toPageable(pageLink); + String searchText = Objects.toString(pageLink.getTextSearch(), ""); + Page page = OtaPackageUtil.getByOtaPackageType( + () -> deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), + () -> deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), + type + ); + return DaoUtil.toPageData(page); } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull( - tenantId, - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(UUID tenantId, UUID deviceProfileId, OtaPackageType type) { + return OtaPackageUtil.getByOtaPackageType( + () -> deviceRepository.countByTenantIdAndDeviceProfileIdAndFirmwareIdIsNull(tenantId, deviceProfileId), + () -> deviceRepository.countByTenantIdAndDeviceProfileIdAndSoftwareIdIsNull(tenantId, deviceProfileId), + type + ); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java deleted file mode 100644 index dab6ce3304..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.sql.firmware; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; - -import java.util.UUID; - -public interface FirmwareInfoRepository extends CrudRepository { - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + - "f.tenantId = :tenantId " + - "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findAllByTenantId(@Param("tenantId") UUID tenantId, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + - "f.tenantId = :tenantId " + - "AND f.deviceProfileId = :deviceProfileId " + - "AND f.type = :type " + - "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + - "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, - @Param("deviceProfileId") UUID deviceProfileId, - @Param("type") FirmwareType type, - @Param("hasData") boolean hasData, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") - FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); - - @Query(value = "SELECT exists(SELECT * " + - "FROM device_profile AS dp " + - "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + - "WHERE dp.id = :deviceProfileId AND " + - "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " + - "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true) - boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java deleted file mode 100644 index 8cae33a057..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.sql.firmware; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.firmware.FirmwareInfoDao; -import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; -import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; - -import java.util.Objects; -import java.util.UUID; - -@Slf4j -@Component -public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao implements FirmwareInfoDao { - - @Autowired - private FirmwareInfoRepository firmwareInfoRepository; - - @Override - protected Class getEntityClass() { - return FirmwareInfoEntity.class; - } - - @Override - protected CrudRepository getCrudRepository() { - return firmwareInfoRepository; - } - - @Override - public FirmwareInfo findById(TenantId tenantId, UUID id) { - return DaoUtil.getData(firmwareInfoRepository.findFirmwareInfoById(id)); - } - - @Override - public FirmwareInfo save(TenantId tenantId, FirmwareInfo firmwareInfo) { - FirmwareInfo savedFirmware = super.save(tenantId, firmwareInfo); - if (firmwareInfo.getId() == null) { - return savedFirmware; - } else { - return findById(tenantId, savedFirmware.getId().getId()); - } - } - - @Override - public PageData findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(firmwareInfoRepository - .findAllByTenantId( - tenantId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); - } - - @Override - public PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { - return DaoUtil.toPageData(firmwareInfoRepository - .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( - tenantId.getId(), - deviceProfileId.getId(), - firmwareType, - hasData, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); - } - - @Override - public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) { - return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name()); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java similarity index 62% rename from dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java index 44b956c298..95737ca48d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java @@ -13,34 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.firmware; +package org.thingsboard.server.dao.sql.ota; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.dao.firmware.FirmwareDao; -import org.thingsboard.server.dao.model.sql.FirmwareEntity; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.dao.ota.OtaPackageDao; +import org.thingsboard.server.dao.model.sql.OtaPackageEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import java.util.UUID; @Slf4j @Component -public class JpaFirmwareDao extends JpaAbstractSearchTextDao implements FirmwareDao { +public class JpaOtaPackageDao extends JpaAbstractSearchTextDao implements OtaPackageDao { @Autowired - private FirmwareRepository firmwareRepository; + private OtaPackageRepository otaPackageRepository; @Override - protected Class getEntityClass() { - return FirmwareEntity.class; + protected Class getEntityClass() { + return OtaPackageEntity.class; } @Override - protected CrudRepository getCrudRepository() { - return firmwareRepository; + protected CrudRepository getCrudRepository() { + return otaPackageRepository; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java new file mode 100644 index 0000000000..0d35c8ee0d --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java @@ -0,0 +1,94 @@ +/** + * Copyright © 2016-2021 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.dao.sql.ota; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.ota.OtaPackageInfoDao; +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; + +import java.util.Objects; +import java.util.UUID; + +@Slf4j +@Component +public class JpaOtaPackageInfoDao extends JpaAbstractSearchTextDao implements OtaPackageInfoDao { + + @Autowired + private OtaPackageInfoRepository otaPackageInfoRepository; + + @Override + protected Class getEntityClass() { + return OtaPackageInfoEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return otaPackageInfoRepository; + } + + @Override + public OtaPackageInfo findById(TenantId tenantId, UUID id) { + return DaoUtil.getData(otaPackageInfoRepository.findOtaPackageInfoById(id)); + } + + @Override + public OtaPackageInfo save(TenantId tenantId, OtaPackageInfo otaPackageInfo) { + OtaPackageInfo savedOtaPackage = super.save(tenantId, otaPackageInfo); + if (otaPackageInfo.getId() == null) { + return savedOtaPackage; + } else { + return findById(tenantId, savedOtaPackage.getId().getId()); + } + } + + @Override + public PageData findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(otaPackageInfoRepository + .findAllByTenantId( + tenantId.getId(), + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { + return DaoUtil.toPageData(otaPackageInfoRepository + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( + tenantId.getId(), + deviceProfileId.getId(), + otaPackageType, + hasData, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { + return otaPackageInfoRepository.isOtaPackageUsed(otaPackageId.getId(), deviceProfileId.getId(), otaPackageType.name()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java new file mode 100644 index 0000000000..9848f83200 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2021 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.dao.sql.ota; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; + +import java.util.UUID; + +public interface OtaPackageInfoRepository extends CrudRepository { + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + "f.tenantId = :tenantId " + + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") + Page findAllByTenantId(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + "f.tenantId = :tenantId " + + "AND f.deviceProfileId = :deviceProfileId " + + "AND f.type = :type " + + "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") + Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId, + @Param("type") OtaPackageType type, + @Param("hasData") boolean hasData, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE f.id = :id") + OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id); + + @Query(value = "SELECT exists(SELECT * " + + "FROM device_profile AS dp " + + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + + "WHERE dp.id = :deviceProfileId AND " + + "(('FIRMWARE' = :type AND (dp.firmware_id = :otaPackageId OR d.firmware_id = :otaPackageId)) " + + "OR ('SOFTWARE' = :type AND (dp.software_id = :otaPackageId or d.software_id = :otaPackageId))))", nativeQuery = true) + boolean isOtaPackageUsed(@Param("otaPackageId") UUID otaPackageId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java similarity index 78% rename from dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java index a969507798..3699005ff2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.firmware; +package org.thingsboard.server.dao.sql.ota; import org.springframework.data.repository.CrudRepository; -import org.thingsboard.server.dao.model.sql.FirmwareEntity; +import org.thingsboard.server.dao.model.sql.OtaPackageEntity; import java.util.UUID; -public interface FirmwareRepository extends CrudRepository { +public interface OtaPackageRepository extends CrudRepository { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 6391ed73e7..c5050467dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -35,7 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; @@ -94,7 +94,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe private ResourceService resourceService; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public Tenant findTenantById(TenantId tenantId) { @@ -150,7 +150,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe ruleChainService.deleteRuleChainsByTenantId(tenantId); apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); resourceService.deleteResourcesByTenantId(tenantId); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); tenantDao.removeById(tenantId, tenantId.getId()); deleteEntityRelations(tenantId, tenantId); } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index fcb7f82ed0..dfc1821f66 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -160,8 +160,8 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid , @@ -176,7 +176,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); CREATE TABLE IF NOT EXISTS device_profile ( @@ -202,8 +202,8 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device ( @@ -222,8 +222,8 @@ CREATE TABLE IF NOT EXISTS device ( software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 30b9a3dbe7..be7e836a65 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -178,8 +178,8 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid , @@ -194,7 +194,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) -- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE ); @@ -221,8 +221,8 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); -- We will use one-to-many relation in the first release and extend this feature in case of user requests @@ -250,8 +250,8 @@ CREATE TABLE IF NOT EXISTS device ( software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 091ab1741e..2aeb44c0be 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -160,7 +160,7 @@ public abstract class AbstractServiceTest { @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; public class IdComparator implements Comparator { @Override diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index 5516cb6181..e53b740cbf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -28,10 +28,9 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -45,7 +44,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @@ -99,7 +98,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(deviceProfile.isDefault(), savedDeviceProfile.isDefault()); Assert.assertEquals(deviceProfile.getDefaultRuleChainId(), savedDeviceProfile.getDefaultRuleChainId()); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -110,7 +109,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); deviceProfile.setFirmwareId(savedFirmware.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index 587fce1ed5..f335ffacd3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -28,10 +28,10 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -46,7 +46,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -189,7 +189,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(device.getDeviceProfileId()); firmware.setType(FIRMWARE); @@ -200,7 +200,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); @@ -223,7 +223,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile); Assert.assertNotNull(savedProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedProfile.getId()); firmware.setType(FIRMWARE); @@ -234,7 +234,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java similarity index 74% rename from dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index bd9e0ed372..e7cee8c878 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -25,10 +25,10 @@ import org.junit.rules.ExpectedException; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -40,9 +40,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { +public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { public static final String TITLE = "My firmware"; private static final String FILE_NAME = "filename.txt"; @@ -52,7 +52,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); - private IdComparator idComparator = new IdComparator<>(); + private IdComparator idComparator = new IdComparator<>(); private TenantId tenantId; @@ -82,7 +82,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @Test public void testSaveFirmware() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -93,7 +93,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Assert.assertNotNull(savedFirmware); Assert.assertNotNull(savedFirmware.getId()); @@ -105,23 +105,23 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmware.getData(), savedFirmware.getData()); savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode()); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testSaveFirmwareInfoAndUpdateWithData() { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = otaPackageService.saveOtaPackageInfo(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -129,7 +129,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwareInfo.getTenantId(), savedFirmwareInfo.getTenantId()); Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); - Firmware firmware = new Firmware(savedFirmwareInfo.getId()); + OtaPackage firmware = new OtaPackage(savedFirmwareInfo.getId()); firmware.setCreatedTime(firmwareInfo.getCreatedTime()); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); @@ -142,24 +142,24 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); - savedFirmwareInfo = firmwareService.findFirmwareInfoById(tenantId, savedFirmwareInfo.getId()); + savedFirmwareInfo = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmwareInfo.getId()); savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); - firmwareService.saveFirmwareInfo(savedFirmwareInfo); + otaPackageService.saveOtaPackageInfo(savedFirmwareInfo); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, firmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, firmware.getId()); firmware.setAdditionalInfo(JacksonUtil.newObjectNode()); Assert.assertEquals(foundFirmware.getTitle(), firmware.getTitle()); Assert.assertTrue(foundFirmware.isHasData()); - firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmwareInfo.getId()); } @Test public void testSaveFirmwareWithEmptyTenant() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); firmware.setTitle(TITLE); @@ -171,13 +171,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware should be assigned to tenant!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage should be assigned to tenant!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyType() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setTitle(TITLE); @@ -190,12 +190,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("Type should be specified!"); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyTitle() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -207,13 +207,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware title should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage title should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyFileName() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -225,13 +225,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware file name should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage file name should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyContentType() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -243,13 +243,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware content type should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage content type should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyData() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -261,13 +261,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware data should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage data should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithInvalidTenant() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(new TenantId(Uuids.timeBased())); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -280,13 +280,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware is referencing to non-existent tenant!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage is referencing to non-existent tenant!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithInvalidDeviceProfileId() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased())); firmware.setType(FIRMWARE); @@ -299,13 +299,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware is referencing to non-existent device profile!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage is referencing to non-existent device profile!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyChecksum() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -317,21 +317,21 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware checksum should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage checksum should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareInfoWithExistingTitleAndVersion() { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - firmwareService.saveFirmwareInfo(firmwareInfo); + otaPackageService.saveOtaPackageInfo(firmwareInfo); - FirmwareInfo newFirmwareInfo = new FirmwareInfo(); + OtaPackageInfo newFirmwareInfo = new OtaPackageInfo(); newFirmwareInfo.setTenantId(tenantId); newFirmwareInfo.setDeviceProfileId(deviceProfileId); newFirmwareInfo.setType(FIRMWARE); @@ -339,13 +339,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { newFirmwareInfo.setVersion(VERSION); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware with such title and version already exists!"); - firmwareService.saveFirmwareInfo(newFirmwareInfo); + thrown.expectMessage("OtaPackage with such title and version already exists!"); + otaPackageService.saveOtaPackageInfo(newFirmwareInfo); } @Test public void testSaveFirmwareWithExistingTitleAndVersion() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -356,9 +356,9 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); - Firmware newFirmware = new Firmware(); + OtaPackage newFirmware = new OtaPackage(); newFirmware.setTenantId(tenantId); newFirmware.setDeviceProfileId(deviceProfileId); newFirmware.setType(FIRMWARE); @@ -371,13 +371,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { newFirmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware with such title and version already exists!"); - firmwareService.saveFirmware(newFirmware); + thrown.expectMessage("OtaPackage with such title and version already exists!"); + otaPackageService.saveOtaPackage(newFirmware); } @Test public void testDeleteFirmwareWithReferenceByDevice() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -388,7 +388,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Device device = new Device(); device.setTenantId(tenantId); @@ -399,17 +399,17 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { try { thrown.expect(DataValidationException.class); - thrown.expectMessage("The firmware referenced by the devices cannot be deleted!"); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + thrown.expectMessage("The otaPackage referenced by the devices cannot be deleted!"); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } finally { deviceService.deleteDevice(tenantId, savedDevice.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @Test public void testUpdateDeviceProfileIdWithReferenceByDevice() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -420,7 +420,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Device device = new Device(); device.setTenantId(tenantId); @@ -431,12 +431,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); savedFirmware.setDeviceProfileId(null); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); } finally { deviceService.deleteDevice(tenantId, savedDevice.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -445,7 +445,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -456,18 +456,18 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); deviceProfileService.saveDeviceProfile(savedDeviceProfile); try { thrown.expect(DataValidationException.class); - thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!"); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + thrown.expectMessage("The otaPackage referenced by the device profile cannot be deleted!"); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } finally { deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -476,7 +476,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -487,25 +487,25 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); deviceProfileService.saveDeviceProfile(savedDeviceProfile); try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); savedFirmware.setDeviceProfileId(null); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); } finally { deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @Test public void testFindFirmwareById() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -516,33 +516,33 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testFindFirmwareInfoById() { - FirmwareInfo firmware = new FirmwareInfo(); + OtaPackageInfo firmware = new OtaPackageInfo(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); - FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); + OtaPackageInfo savedFirmware = otaPackageService.saveOtaPackageInfo(firmware); - FirmwareInfo foundFirmware = firmwareService.findFirmwareInfoById(tenantId, savedFirmware.getId()); + OtaPackageInfo foundFirmware = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testDeleteFirmware() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -553,20 +553,20 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); - foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); + foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNull(foundFirmware); } @Test public void testFindTenantFirmwaresByTenantId() { - List firmwares = new ArrayList<>(); + List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -578,16 +578,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - FirmwareInfo info = new FirmwareInfo(firmwareService.saveFirmware(firmware)); + OtaPackageInfo info = new OtaPackageInfo(otaPackageService.saveOtaPackage(firmware)); info.setHasData(true); firmwares.add(info); } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); - PageData pageData; + PageData pageData; do { - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -599,19 +599,19 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); pageLink = new PageLink(31); - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); } @Test public void testFindTenantFirmwaresByTenantIdAndHasData() { - List firmwares = new ArrayList<>(); + List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); @@ -622,14 +622,14 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmwareInfo.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmwareInfo.setChecksum(CHECKSUM); firmwareInfo.setDataSize((long) DATA.array().length); - firmwares.add(firmwareService.saveFirmwareInfo(firmwareInfo)); + firmwares.add(otaPackageService.saveOtaPackageInfo(firmwareInfo)); } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); - PageData pageData; + PageData pageData; do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -642,7 +642,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); firmwares.forEach(f -> { - Firmware firmware = new Firmware(f.getId()); + OtaPackage firmware = new OtaPackage(f.getId()); firmware.setCreatedTime(f.getCreatedTime()); firmware.setTenantId(f.getTenantId()); firmware.setDeviceProfileId(deviceProfileId); @@ -655,14 +655,14 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); firmware.setDataSize((long) DATA.array().length); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); f.setHasData(true); }); loadedFirmwares = new ArrayList<>(); pageLink = new PageLink(16); do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -674,10 +674,10 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); pageLink = new PageLink(31); - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java similarity index 83% rename from dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java index a89414e3a2..59ac4f76bf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.BaseFirmwareServiceTest; +import org.thingsboard.server.dao.service.BaseOtaPackageServiceTest; import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest -public class FirmwareServiceSqlTest extends BaseFirmwareServiceTest { +public class OtaPackageServiceSqlTest extends BaseOtaPackageServiceTest { } diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 2805c78fdd..74eb4f43f0 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -36,8 +36,8 @@ caffeine.specs.tenantProfiles.maxSize=100000 caffeine.specs.deviceProfiles.timeToLiveInMinutes=1440 caffeine.specs.deviceProfiles.maxSize=100000 -caffeine.specs.firmwares.timeToLiveInMinutes=1440 -caffeine.specs.firmwares.maxSize=100000 +caffeine.specs.otaPackages.timeToLiveInMinutes=1440 +caffeine.specs.otaPackages.maxSize=100000 caffeine.specs.edges.timeToLiveInMinutes=1440 caffeine.specs.edges.maxSize=100000 diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index f65316f2c1..726b4ba412 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -29,7 +29,7 @@ DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; -DROP TABLE IF EXISTS firmware; +DROP TABLE IF EXISTS ota_package; DROP TABLE IF EXISTS edge; DROP TABLE IF EXISTS edge_event; DROP FUNCTION IF EXISTS to_uuid; From 874f5e84485ec6cc1f41e2a4f625fc9dc3c66d0b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 31 May 2021 18:15:31 +0300 Subject: [PATCH 10/75] UI: Rename firmware to OtaPackage --- ui-ngx/src/app/core/http/entity.service.ts | 20 +-- ui-ngx/src/app/core/http/firmware.service.ts | 123 ------------------ .../src/app/core/http/ota-package.service.ts | 123 ++++++++++++++++++ ui-ngx/src/app/core/services/menu.service.ts | 8 +- ...device-profile-autocomplete.component.html | 1 + .../device-profile-autocomplete.component.ts | 3 + .../profile/device-profile.component.html | 12 +- .../profile/device-profile.component.ts | 4 +- .../app/modules/home/models/services.map.ts | 4 +- .../home/pages/device/device.component.html | 12 +- .../home/pages/device/device.component.ts | 4 +- .../firmware/firmware-table-config.resolve.ts | 115 ---------------- .../modules/home/pages/home-pages.module.ts | 4 +- .../ota-update-routing.module.ts} | 14 +- .../ota-update-table-config.resolve.ts | 116 +++++++++++++++++ .../ota-update.component.html} | 80 ++++++------ .../ota-update.component.ts} | 44 ++++--- .../ota-update.module.ts} | 10 +- .../ota-package-autocomplete.component.html} | 27 ++-- .../ota-package-autocomplete.component.ts} | 110 +++++++--------- ui-ngx/src/app/shared/models/constants.ts | 1 + ui-ngx/src/app/shared/models/device.models.ts | 10 +- .../app/shared/models/entity-type.models.ts | 20 +-- .../id/{firmware-id.ts => ota-package-id.ts} | 4 +- ...rmware.models.ts => ota-package.models.ts} | 43 ++++-- ui-ngx/src/app/shared/shared.module.ts | 6 +- .../assets/locale/locale.constant-en_US.json | 96 +++++++------- 27 files changed, 521 insertions(+), 493 deletions(-) delete mode 100644 ui-ngx/src/app/core/http/firmware.service.ts create mode 100644 ui-ngx/src/app/core/http/ota-package.service.ts delete mode 100644 ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts rename ui-ngx/src/app/modules/home/pages/{firmware/firmware-routing.module.ts => ota-update/ota-update-routing.module.ts} (78%) create mode 100644 ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts rename ui-ngx/src/app/modules/home/pages/{firmware/firmwares.component.html => ota-update/ota-update.component.html} (60%) rename ui-ngx/src/app/modules/home/pages/{firmware/firmwares.component.ts => ota-update/ota-update.component.ts} (74%) rename ui-ngx/src/app/modules/home/pages/{firmware/firmware.module.ts => ota-update/ota-update.module.ts} (79%) rename ui-ngx/src/app/shared/components/{firmware/firmware-autocomplete.component.html => ota-package/ota-package-autocomplete.component.html} (62%) rename ui-ngx/src/app/shared/components/{firmware/firmware-autocomplete.component.ts => ota-package/ota-package-autocomplete.component.ts} (59%) rename ui-ngx/src/app/shared/models/id/{firmware-id.ts => ota-package-id.ts} (90%) rename ui-ngx/src/app/shared/models/{firmware.models.ts => ota-package.models.ts} (58%) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index c5ef4b8db4..c58e5ac909 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -75,12 +75,12 @@ import { StringOperation } from '@shared/models/query/query.models'; import { alarmFields } from '@shared/models/alarm.models'; -import { FirmwareService } from '@core/http/firmware.service'; -import { EdgeService } from "@core/http/edge.service"; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { EdgeService } from '@core/http/edge.service'; import { Edge, EdgeEventType } from '@shared/models/edge.models'; -import { RuleChainType } from "@shared/models/rule-chain.models"; -import { WidgetService } from "@core/http/widget.service"; -import { DeviceProfileService } from "@core/http/device-profile.service"; +import { RuleChainType } from '@shared/models/rule-chain.models'; +import { WidgetService } from '@core/http/widget.service'; +import { DeviceProfileService } from '@core/http/device-profile.service'; @Injectable({ providedIn: 'root' @@ -101,7 +101,7 @@ export class EntityService { private dashboardService: DashboardService, private entityRelationService: EntityRelationService, private attributeService: AttributeService, - private firmwareService: FirmwareService, + private otaPackageService: OtaPackageService, private widgetService: WidgetService, private deviceProfileService: DeviceProfileService, private utils: UtilsService @@ -142,8 +142,8 @@ export class EntityService { case EntityType.ALARM: console.error('Get Alarm Entity is not implemented!'); break; - case EntityType.FIRMWARE: - observable = this.firmwareService.getFirmwareInfo(entityId, config); + case EntityType.OTA_PACKAGE: + observable = this.otaPackageService.getOtaPackageInfo(entityId, config); break; } return observable; @@ -359,9 +359,9 @@ export class EntityService { case EntityType.ALARM: console.error('Get Alarm Entities is not implemented!'); break; - case EntityType.FIRMWARE: + case EntityType.OTA_PACKAGE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.firmwareService.getFirmwares(pageLink, config); + entitiesObservable = this.otaPackageService.getOtaPackages(pageLink, config); break; } return entitiesObservable; diff --git a/ui-ngx/src/app/core/http/firmware.service.ts b/ui-ngx/src/app/core/http/firmware.service.ts deleted file mode 100644 index 1fe51c5f68..0000000000 --- a/ui-ngx/src/app/core/http/firmware.service.ts +++ /dev/null @@ -1,123 +0,0 @@ -/// -/// Copyright © 2016-2021 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. -/// - -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { PageLink } from '@shared/models/page/page-link'; -import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; -import { Observable } from 'rxjs'; -import { PageData } from '@shared/models/page/page-data'; -import { ChecksumAlgorithm, Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; -import { catchError, map, mergeMap } from 'rxjs/operators'; -import { deepClone } from '@core/utils'; - -@Injectable({ - providedIn: 'root' -}) -export class FirmwareService { - constructor( - private http: HttpClient - ) { - - } - - public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); - } - - public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType, - hasData = true, config?: RequestConfig): Observable> { - const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; - return this.http.get>(url, defaultHttpOptionsFromConfig(config)); - } - - public getFirmware(firmwareId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - - public getFirmwareInfo(firmwareId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/firmware/info/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - - public downloadFirmware(firmwareId: string): Observable { - return this.http.get(`/api/firmware/${firmwareId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( - map((response) => { - const headers = response.headers; - const filename = headers.get('x-filename'); - const contentType = headers.get('content-type'); - const linkElement = document.createElement('a'); - try { - const blob = new Blob([response.body], { type: contentType }); - const url = URL.createObjectURL(blob); - linkElement.setAttribute('href', url); - linkElement.setAttribute('download', filename); - const clickEvent = new MouseEvent('click', - { - view: window, - bubbles: true, - cancelable: false - } - ); - linkElement.dispatchEvent(clickEvent); - return null; - } catch (e) { - throw e; - } - }) - ); - } - - public saveFirmware(firmware: Firmware, config?: RequestConfig): Observable { - if (!firmware.file) { - return this.saveFirmwareInfo(firmware, config); - } - const firmwareInfo = deepClone(firmware); - delete firmwareInfo.file; - delete firmwareInfo.checksum; - delete firmwareInfo.checksumAlgorithm; - return this.saveFirmwareInfo(firmwareInfo, config).pipe( - mergeMap(res => { - return this.uploadFirmwareFile(res.id.id, firmware.file, firmware.checksumAlgorithm, firmware.checksum).pipe( - catchError(() => this.deleteFirmware(res.id.id)) - ); - }) - ); - } - - public saveFirmwareInfo(firmware: FirmwareInfo, config?: RequestConfig): Observable { - return this.http.post('/api/firmware', firmware, defaultHttpOptionsFromConfig(config)); - } - - public uploadFirmwareFile(firmwareId: string, file: File, checksumAlgorithm: ChecksumAlgorithm, - checksum?: string, config?: RequestConfig): Observable { - if (!config) { - config = {}; - } - const formData = new FormData(); - formData.append('file', file); - let url = `/api/firmware/${firmwareId}?checksumAlgorithm=${checksumAlgorithm}`; - if (checksum) { - url += `&checksum=${checksum}`; - } - return this.http.post(url, formData, - defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); - } - - public deleteFirmware(firmwareId: string, config?: RequestConfig) { - return this.http.delete(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - -} diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts new file mode 100644 index 0000000000..34993e5318 --- /dev/null +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -0,0 +1,123 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; +import { Observable } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { ChecksumAlgorithm, OtaPackage, OtaPackageInfo, OtaUpdateType } from '@shared/models/ota-package.models'; +import { catchError, map, mergeMap } from 'rxjs/operators'; +import { deepClone } from '@core/utils'; + +@Injectable({ + providedIn: 'root' +}) +export class OtaPackageService { + constructor( + private http: HttpClient + ) { + + } + + public getOtaPackages(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/otaPackages${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, + hasData = true, config?: RequestConfig): Observable> { + const url = `/api/otaPackages/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackage(otaPackageId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/otaPackages/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackageInfo(otaPackageId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/otaPackage/info/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + + public downloadOtaPackage(otaPackageId: string): Observable { + return this.http.get(`/api/otaPackage/${otaPackageId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( + map((response) => { + const headers = response.headers; + const filename = headers.get('x-filename'); + const contentType = headers.get('content-type'); + const linkElement = document.createElement('a'); + try { + const blob = new Blob([response.body], { type: contentType }); + const url = URL.createObjectURL(blob); + linkElement.setAttribute('href', url); + linkElement.setAttribute('download', filename); + const clickEvent = new MouseEvent('click', + { + view: window, + bubbles: true, + cancelable: false + } + ); + linkElement.dispatchEvent(clickEvent); + return null; + } catch (e) { + throw e; + } + }) + ); + } + + public saveOtaPackage(otaPackage: OtaPackage, config?: RequestConfig): Observable { + if (!otaPackage.file) { + return this.saveOtaPackageInfo(otaPackage, config); + } + const otaPackageInfo = deepClone(otaPackage); + delete otaPackageInfo.file; + delete otaPackageInfo.checksum; + delete otaPackageInfo.checksumAlgorithm; + return this.saveOtaPackageInfo(otaPackageInfo, config).pipe( + mergeMap(res => { + return this.uploadOtaPackageFile(res.id.id, otaPackage.file, otaPackage.checksumAlgorithm, otaPackage.checksum).pipe( + catchError(() => this.deleteOtaPackage(res.id.id)) + ); + }) + ); + } + + public saveOtaPackageInfo(otaPackageInfo: OtaPackageInfo, config?: RequestConfig): Observable { + return this.http.post('/api/otaPackage', otaPackageInfo, defaultHttpOptionsFromConfig(config)); + } + + public uploadOtaPackageFile(otaPackageId: string, file: File, checksumAlgorithm: ChecksumAlgorithm, + checksum?: string, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + let url = `/api/otaPackage/${otaPackageId}?checksumAlgorithm=${checksumAlgorithm}`; + if (checksum) { + url += `&checksum=${checksum}`; + } + return this.http.post(url, formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public deleteOtaPackage(otaPackageId: string, config?: RequestConfig) { + return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + +} diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 07a6d8f237..806447f112 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -276,9 +276,9 @@ export class MenuService { }, { id: guid(), - name: 'firmware.firmware', + name: 'ota-update.ota-updates', type: 'link', - path: '/firmwares', + path: '/otaUpdates', icon: 'memory' }, { @@ -423,9 +423,9 @@ export class MenuService { path: '/deviceProfiles' }, { - name: 'firmware.firmware', + name: 'ota-update.ota-updates', icon: 'memory', - path: '/firmwares' + path: '/otaUpdates' } ] }, diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index 17caf4f0d7..bd6f92b175 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -66,4 +66,5 @@ {{ 'device-profile.device-profile-required' | translate }} + {{ hint | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts index 3a0ae491da..a2fa8d1ed3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts @@ -91,6 +91,9 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, @Input() disabled: boolean; + @Input() + hint: string; + @Output() deviceProfileUpdated = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 325bc20419..5fa6c1acdb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -67,18 +67,18 @@ [queueType]="serviceType" formControlName="defaultQueueName"> - - - + - + device-profile.type diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index 6db89d818e..efaa234adb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -40,7 +40,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { ServiceType } from '@shared/models/queue.models'; import { EntityId } from '@shared/models/id/entity-id'; -import { FirmwareType } from '@shared/models/firmware.models'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; import { DashboardId } from '@shared/models/id/dashboard-id'; @Component({ @@ -71,7 +71,7 @@ export class DeviceProfileComponent extends EntityComponent { deviceProfileId: EntityId; - firmwareTypes = FirmwareType; + otaUpdateType = OtaUpdateType; constructor(protected store: Store, protected translate: TranslateService, diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index c518249b77..4b65083038 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -35,7 +35,7 @@ import { Router } from '@angular/router'; import { BroadcastService } from '@core/services/broadcast.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { DeviceProfileService } from '@core/http/device-profile.service'; -import { FirmwareService } from '@core/http/firmware.service'; +import { OtaPackageService } from '@core/http/ota-package.service'; export const ServicesMap = new Map>( [ @@ -59,6 +59,6 @@ export const ServicesMap = new Map>( ['router', Router], ['importExport', ImportExportService], ['deviceProfileService', DeviceProfileService], - ['firmwareService', FirmwareService] + ['otaPackageService', OtaPackageService] ] ); diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index f09437662a..2dd6486eeb 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -101,18 +101,18 @@ device.label - - - + - + diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index d7f21fb2fc..1ed9f784f1 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -34,7 +34,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { Subject } from 'rxjs'; -import { FirmwareType } from '@shared/models/firmware.models'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; @Component({ selector: 'tb-device', @@ -49,7 +49,7 @@ export class DeviceComponent extends EntityComponent { deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge'; - firmwareTypes = FirmwareType; + otaUpdateType = OtaUpdateType; constructor(protected store: Store, protected translate: TranslateService, diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts deleted file mode 100644 index 23efe2117b..0000000000 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts +++ /dev/null @@ -1,115 +0,0 @@ -/// -/// Copyright © 2016-2021 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. -/// - -import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; -import { - DateEntityTableColumn, - EntityTableColumn, - EntityTableConfig -} from '@home/models/entity/entities-table-config.models'; -import { - ChecksumAlgorithmTranslationMap, - Firmware, - FirmwareInfo, - FirmwareTypeTranslationMap -} from '@shared/models/firmware.models'; -import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { TranslateService } from '@ngx-translate/core'; -import { DatePipe } from '@angular/common'; -import { FirmwareService } from '@core/http/firmware.service'; -import { PageLink } from '@shared/models/page/page-link'; -import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; -import { EntityAction } from '@home/models/entity/entity-component.models'; -import { FileSizePipe } from '@shared/pipe/file-size.pipe'; - -@Injectable() -export class FirmwareTableConfigResolve implements Resolve> { - - private readonly config: EntityTableConfig = new EntityTableConfig(); - - constructor(private translate: TranslateService, - private datePipe: DatePipe, - private firmwareService: FirmwareService, - private fileSize: FileSizePipe) { - this.config.entityType = EntityType.FIRMWARE; - this.config.entityComponent = FirmwaresComponent; - this.config.entityTranslations = entityTypeTranslations.get(EntityType.FIRMWARE); - this.config.entityResources = entityTypeResources.get(EntityType.FIRMWARE); - - this.config.entityTitle = (firmware) => firmware ? firmware.title : ''; - - this.config.columns.push( - new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), - new EntityTableColumn('title', 'firmware.title', '25%'), - new EntityTableColumn('version', 'firmware.version', '25%'), - new EntityTableColumn('type', 'firmware.type', '25%', entity => { - return this.translate.instant(FirmwareTypeTranslationMap.get(entity.type)); - }), - new EntityTableColumn('fileName', 'firmware.file-name', '25%'), - new EntityTableColumn('dataSize', 'firmware.file-size', '70px', entity => { - return this.fileSize.transform(entity.dataSize || 0); - }), - new EntityTableColumn('checksum', 'firmware.checksum', '540px', entity => { - return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; - }, () => ({}), false) - ); - - this.config.cellActionDescriptors.push( - { - name: this.translate.instant('firmware.download'), - icon: 'file_download', - isEnabled: (firmware) => firmware.hasData, - onAction: ($event, entity) => this.exportFirmware($event, entity) - } - ); - - this.config.deleteEntityTitle = firmware => this.translate.instant('firmware.delete-firmware-title', - { firmwareTitle: firmware.title }); - this.config.deleteEntityContent = () => this.translate.instant('firmware.delete-firmware-text'); - this.config.deleteEntitiesTitle = count => this.translate.instant('firmware.delete-firmwares-title', {count}); - this.config.deleteEntitiesContent = () => this.translate.instant('firmware.delete-firmwares-text'); - - this.config.entitiesFetchFunction = pageLink => this.firmwareService.getFirmwares(pageLink); - this.config.loadEntity = id => this.firmwareService.getFirmwareInfo(id.id); - this.config.saveEntity = firmware => this.firmwareService.saveFirmware(firmware); - this.config.deleteEntity = id => this.firmwareService.deleteFirmware(id.id); - - this.config.onEntityAction = action => this.onFirmwareAction(action); - } - - resolve(): EntityTableConfig { - this.config.tableTitle = this.translate.instant('firmware.firmware'); - return this.config; - } - - exportFirmware($event: Event, firmware: FirmwareInfo) { - if ($event) { - $event.stopPropagation(); - } - this.firmwareService.downloadFirmware(firmware.id.id).subscribe(); - } - - onFirmwareAction(action: EntityAction): boolean { - switch (action.action) { - case 'uploadFirmware': - this.exportFirmware(action.event, action.entity); - return true; - } - return false; - } - -} diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts index 050b71db83..834321fc49 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts @@ -35,7 +35,7 @@ import { modulesMap } from '../../common/modules-map'; import { DeviceProfileModule } from './device-profile/device-profile.module'; import { ApiUsageModule } from '@home/pages/api-usage/api-usage.module'; import { EdgeModule } from '@home/pages/edge/edge.module'; -import { FirmwareModule } from '@home/pages/firmware/firmware.module'; +import { OtaUpdateModule } from '@home/pages/ota-update/ota-update.module'; @NgModule({ exports: [ @@ -55,7 +55,7 @@ import { FirmwareModule } from '@home/pages/firmware/firmware.module'; DashboardModule, AuditLogModule, ApiUsageModule, - FirmwareModule, + OtaUpdateModule, UserModule ], providers: [ diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts similarity index 78% rename from ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts index 688f3bfc1f..6945244c47 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts @@ -18,22 +18,22 @@ import { RouterModule, Routes } from '@angular/router'; import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; import { Authority } from '@shared/models/authority.enum'; import { NgModule } from '@angular/core'; -import { FirmwareTableConfigResolve } from '@home/pages/firmware/firmware-table-config.resolve'; +import { OtaUpdateTableConfigResolve } from '@home/pages/ota-update/ota-update-table-config.resolve'; const routes: Routes = [ { - path: 'firmwares', + path: 'otaUpdates', component: EntitiesTableComponent, data: { auth: [Authority.TENANT_ADMIN], - title: 'firmware.firmware', + title: 'ota-update.ota-updates', breadcrumb: { - label: 'firmware.firmware', + label: 'ota-update.ota-updates', icon: 'memory' } }, resolve: { - entitiesTableConfig: FirmwareTableConfigResolve + entitiesTableConfig: OtaUpdateTableConfigResolve } } ]; @@ -42,7 +42,7 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - FirmwareTableConfigResolve + OtaUpdateTableConfigResolve ] }) -export class FirmwareRoutingModule{ } +export class OtaUpdateRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts new file mode 100644 index 0000000000..b22d43da46 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -0,0 +1,116 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { + ChecksumAlgorithmTranslationMap, + OtaPackage, + OtaPackageInfo, + OtaUpdateTypeTranslationMap +} from '@shared/models/ota-package.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { PageLink } from '@shared/models/page/page-link'; +import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { FileSizePipe } from '@shared/pipe/file-size.pipe'; + +@Injectable() +export class OtaUpdateTableConfigResolve implements Resolve> { + + private readonly config: EntityTableConfig = + new EntityTableConfig(); + + constructor(private translate: TranslateService, + private datePipe: DatePipe, + private otaPackageService: OtaPackageService, + private fileSize: FileSizePipe) { + this.config.entityType = EntityType.OTA_PACKAGE; + this.config.entityComponent = OtaUpdateComponent; + this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE); + this.config.entityResources = entityTypeResources.get(EntityType.OTA_PACKAGE); + + this.config.entityTitle = (otaPackage) => otaPackage ? otaPackage.title : ''; + + this.config.columns.push( + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), + new EntityTableColumn('title', 'ota-update.title', '25%'), + new EntityTableColumn('version', 'ota-update.version', '25%'), + new EntityTableColumn('type', 'ota-update.package-type', '25%', entity => { + return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); + }), + new EntityTableColumn('fileName', 'ota-update.file-name', '25%'), + new EntityTableColumn('dataSize', 'ota-update.file-size', '70px', entity => { + return this.fileSize.transform(entity.dataSize || 0); + }), + new EntityTableColumn('checksum', 'ota-update.checksum', '540px', entity => { + return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; + }, () => ({}), false) + ); + + this.config.cellActionDescriptors.push( + { + name: this.translate.instant('ota-update.download'), + icon: 'file_download', + isEnabled: (otaPackage) => otaPackage.hasData, + onAction: ($event, entity) => this.exportPackage($event, entity) + } + ); + + this.config.deleteEntityTitle = otaPackage => this.translate.instant('ota-update.delete-ota-update-title', + { title: otaPackage.title }); + this.config.deleteEntityContent = () => this.translate.instant('ota-update.delete-ota-update-text'); + this.config.deleteEntitiesTitle = count => this.translate.instant('ota-update.delete-ota-updates-title', {count}); + this.config.deleteEntitiesContent = () => this.translate.instant('ota-update.delete-ota-updates-text'); + + this.config.entitiesFetchFunction = pageLink => this.otaPackageService.getOtaPackages(pageLink); + this.config.loadEntity = id => this.otaPackageService.getOtaPackageInfo(id.id); + this.config.saveEntity = otaPackage => this.otaPackageService.saveOtaPackage(otaPackage); + this.config.deleteEntity = id => this.otaPackageService.deleteOtaPackage(id.id); + + this.config.onEntityAction = action => this.onPackageAction(action); + } + + resolve(): EntityTableConfig { + this.config.tableTitle = this.translate.instant('ota-update.packages-repository'); + return this.config; + } + + exportPackage($event: Event, otaPackageInfo: OtaPackageInfo) { + if ($event) { + $event.stopPropagation(); + } + this.otaPackageService.downloadOtaPackage(otaPackageInfo.id.id).subscribe(); + } + + onPackageAction(action: EntityAction): boolean { + switch (action.action) { + case 'uploadPackage': + this.exportPackage(action.event, action.entity); + return true; + } + return false; + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html similarity index 60% rename from ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index b608604d2f..77a68f72e5 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -18,114 +18,112 @@
-
+
-
- firmware.warning-after-save-no-edit +
- firmware.title + ota-update.title - {{ 'firmware.title-required' | translate }} + {{ 'ota-update.title-required' | translate }} - firmware.version + ota-update.version - {{ 'firmware.version-required' | translate }} + {{ 'ota-update.version-required' | translate }}
-
- - firmware.type - - - - {{ firmwareTypeTranslationMap.get(firmwareType) | translate }} - - - - - -
+ + + + ota-update.package-type + + + {{ otaUpdateTypeTranslationMap.get(packageType) | translate }} + + + +
ota-update.warning-after-save-no-edit
- firmware.checksum-algorithm - - + ota-update.checksum-algorithm + {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} - firmware.checksum + ota-update.checksum
-
+
+ dropLabel="{{'ota-update.drop-file' | translate}}">
- firmware.file-name + ota-update.file-name - firmware.file-size-bytes + ota-update.file-size-bytes - firmware.content-type + ota-update.content-type
- firmware.description + ota-update.description
diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts similarity index 74% rename from ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index e2283a588f..fd4cd7ae27 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -25,29 +25,29 @@ import { EntityComponent } from '@home/components/entity/entity.component'; import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, - Firmware, - FirmwareType, - FirmwareTypeTranslationMap -} from '@shared/models/firmware.models'; + OtaPackage, + OtaUpdateType, + OtaUpdateTypeTranslationMap +} from '@shared/models/ota-package.models'; import { ActionNotificationShow } from '@core/notification/notification.actions'; @Component({ - selector: 'tb-firmware', - templateUrl: './firmwares.component.html' + selector: 'tb-ota-update', + templateUrl: './ota-update.component.html' }) -export class FirmwaresComponent extends EntityComponent implements OnInit, OnDestroy { +export class OtaUpdateComponent extends EntityComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); checksumAlgorithms = Object.values(ChecksumAlgorithm); checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap; - firmwareTypes = Object.values(FirmwareType); - firmwareTypeTranslationMap = FirmwareTypeTranslationMap; + packageTypes = Object.values(OtaUpdateType); + otaUpdateTypeTranslationMap = OtaUpdateTypeTranslationMap; constructor(protected store: Store, protected translate: TranslateService, - @Inject('entity') protected entityValue: Firmware, - @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, + @Inject('entity') protected entityValue: OtaPackage, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, public fb: FormBuilder) { super(store, fb, entityValue, entitiesTableConfigValue); } @@ -66,12 +66,12 @@ export class FirmwaresComponent extends EntityComponent implements OnI } } - buildForm(entity: Firmware): FormGroup { + buildForm(entity: OtaPackage): FormGroup { const form = this.fb.group({ title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], - type: [entity?.type ? entity.type : FirmwareType.FIRMWARE, [Validators.required]], - deviceProfileId: [entity ? entity.deviceProfileId : null], + type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required], + deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)], additionalInfo: this.fb.group( @@ -90,7 +90,7 @@ export class FirmwaresComponent extends EntityComponent implements OnI return form; } - updateForm(entity: Firmware) { + updateForm(entity: OtaPackage) { this.entityForm.patchValue({ title: entity.title, version: entity.version, @@ -105,12 +105,18 @@ export class FirmwaresComponent extends EntityComponent implements OnI description: entity.additionalInfo ? entity.additionalInfo.description : '' } }); + if (!this.isAdd && this.entityForm.enabled) { + this.entityForm.disable({emitEvent: false}); + this.entityForm.get('additionalInfo').enable({emitEvent: false}); + // this.entityForm.get('dataSize').disable({emitEvent: false}); + // this.entityForm.get('contentType').disable({emitEvent: false}); + } } - onFirmwareIdCopied() { + onPackageIdCopied() { this.store.dispatch(new ActionNotificationShow( { - message: this.translate.instant('firmware.idCopiedMessage'), + message: this.translate.instant('ota-update.idCopiedMessage'), type: 'success', duration: 750, verticalPosition: 'bottom', @@ -118,10 +124,10 @@ export class FirmwaresComponent extends EntityComponent implements OnI })); } - onFirmwareChecksumCopied() { + onPackageChecksumCopied() { this.store.dispatch(new ActionNotificationShow( { - message: this.translate.instant('firmware.checksum-copied-message'), + message: this.translate.instant('ota-update.checksum-copied-message'), type: 'success', duration: 750, verticalPosition: 'bottom', diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts similarity index 79% rename from ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts index ab1b8343b2..58beef85ec 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts @@ -18,18 +18,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { HomeComponentsModule } from '@home/components/home-components.module'; -import { FirmwareRoutingModule } from '@home/pages/firmware/firmware-routing.module'; -import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; +import { OtaUpdateRoutingModule } from '@home/pages/ota-update/ota-update-routing.module'; +import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; @NgModule({ declarations: [ - FirmwaresComponent + OtaUpdateComponent ], imports: [ CommonModule, SharedModule, HomeComponentsModule, - FirmwareRoutingModule + OtaUpdateRoutingModule ] }) -export class FirmwareModule { } +export class OtaUpdateModule { } diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html similarity index 62% rename from ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html rename to ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html index 7bb434e7d1..51c291006d 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html @@ -15,40 +15,41 @@ limitations under the License. --> - + - - - + #packageAutocomplete="matAutocomplete" + [displayWith]="displayPackageFn"> + + - +
- {{ notFoundFirmware | translate }} + {{ notFoundPackage | translate }}
- {{ translate.get(notMatchingFirmware, + {{ translate.get(notMatchingPackage, {entity: truncate.transform(searchText, true, 6, '...')}) | async }}
- + {{ requiredErrorText | translate }} + {{ hintText | translate }}
diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts similarity index 59% rename from ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts rename to ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index a3aeafbd5e..25df909a8c 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -28,29 +28,29 @@ import { BaseData } from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service'; import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; -import { FirmwareService } from '@core/http/firmware.service'; +import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models'; +import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; @Component({ - selector: 'tb-firmware-autocomplete', - templateUrl: './firmware-autocomplete.component.html', + selector: 'tb-ota-package-autocomplete', + templateUrl: './ota-package-autocomplete.component.html', styleUrls: [], providers: [{ provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => FirmwareAutocompleteComponent), + useExisting: forwardRef(() => OtaPackageAutocompleteComponent), multi: true }] }) -export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnInit { +export class OtaPackageAutocompleteComponent implements ControlValueAccessor, OnInit { - firmwareFormGroup: FormGroup; + otaPackageFormGroup: FormGroup; modelValue: string | EntityId | null; @Input() - type = FirmwareType.FIRMWARE; + type = OtaUpdateType.FIRMWARE; @Input() deviceProfileId: string; @@ -78,42 +78,24 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn @Input() disabled: boolean; - @ViewChild('firmwareInput', {static: true}) firmwareInput: ElementRef; - @ViewChild('firmwareInput', {read: MatAutocompleteTrigger}) firmwareAutocomplete: MatAutocompleteTrigger; + @ViewChild('packageInput', {static: true}) packageInput: ElementRef; - filteredFirmwares: Observable>; + filteredPackages: Observable>; searchText = ''; private dirty = false; - private firmwareTypeTranslation = new Map( - [ - [FirmwareType.FIRMWARE, { - label: 'firmware.firmware', - required: 'firmware.firmware-required', - noFound: 'firmware.no-firmware-text', - noMatching: 'firmware.no-firmware-matching' - }], - [FirmwareType.SOFTWARE, { - label: 'firmware.software', - required: 'firmware.software-required', - noFound: 'firmware.no-software-text', - noMatching: 'firmware.no-software-matching' - }] - ] - ); - private propagateChange = (v: any) => { }; constructor(private store: Store, public translate: TranslateService, public truncate: TruncatePipe, private entityService: EntityService, - private firmwareService: FirmwareService, + private otaPackageService: OtaPackageService, private fb: FormBuilder) { - this.firmwareFormGroup = this.fb.group({ - firmwareId: [null] + this.otaPackageFormGroup = this.fb.group({ + packageId: [null] }); } @@ -125,7 +107,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } ngOnInit() { - this.filteredFirmwares = this.firmwareFormGroup.get('firmwareId').valueChanges + this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges .pipe( tap(value => { let modelValue; @@ -140,7 +122,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } }), map(value => value ? (typeof value === 'string' ? value : value.title) : ''), - mergeMap(name => this.fetchFirmware(name)), + mergeMap(name => this.fetchPackages(name)), share() ); } @@ -149,7 +131,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } getCurrentEntity(): BaseData | null { - const currentRuleChain = this.firmwareFormGroup.get('firmwareId').value; + const currentRuleChain = this.otaPackageFormGroup.get('packageId').value; if (currentRuleChain && typeof currentRuleChain !== 'string') { return currentRuleChain as BaseData; } else { @@ -160,9 +142,9 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { - this.firmwareFormGroup.disable({emitEvent: false}); + this.otaPackageFormGroup.disable({emitEvent: false}); } else { - this.firmwareFormGroup.enable({emitEvent: false}); + this.otaPackageFormGroup.enable({emitEvent: false}); } } @@ -173,21 +155,21 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn writeValue(value: string | EntityId | null): void { this.searchText = ''; if (value != null && value !== '') { - let firmwareId = ''; + let packageId = ''; if (typeof value === 'string') { - firmwareId = value; + packageId = value; } else if (value.entityType && value.id) { - firmwareId = value.id; + packageId = value.id; } - if (firmwareId !== '') { - this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe( + if (packageId !== '') { + this.entityService.getEntity(EntityType.OTA_PACKAGE, packageId, {ignoreLoading: true, ignoreErrors: true}).subscribe( (entity) => { this.modelValue = this.useFullEntityId ? entity.id : entity.id.id; - this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue(entity, {emitEvent: false}); }, () => { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); if (value !== null) { this.propagateChange(this.modelValue); } @@ -195,25 +177,25 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn ); } else { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); this.propagateChange(null); } } else { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); } this.dirty = true; } onFocus() { if (this.dirty) { - this.firmwareFormGroup.get('firmwareId').updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.otaPackageFormGroup.get('packageId').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.dirty = false; } } reset() { - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); } updateView(value: string | null) { @@ -223,47 +205,51 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } } - displayFirmwareFn(firmware?: FirmwareInfo): string | undefined { - return firmware ? `${firmware.title} (${firmware.version})` : undefined; + displayPackageFn(packageInfo?: OtaPackageInfo): string | undefined { + return packageInfo ? `${packageInfo.title} (${packageInfo.version})` : undefined; } - fetchFirmware(searchText?: string): Observable> { + fetchPackages(searchText?: string): Observable> { this.searchText = searchText; const pageLink = new PageLink(50, 0, searchText, { property: 'title', direction: Direction.ASC }); - return this.firmwareService.getFirmwaresInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, + return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, true, {ignoreLoading: true}).pipe( map((data) => data && data.data.length ? data.data : null) ); } clear() { - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: true}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: true}); setTimeout(() => { - this.firmwareInput.nativeElement.blur(); - this.firmwareInput.nativeElement.focus(); + this.packageInput.nativeElement.blur(); + this.packageInput.nativeElement.focus(); }, 0); } get placeholderText(): string { - return this.labelText || this.firmwareTypeTranslation.get(this.type).label; + return this.labelText || OtaUpdateTranslation.get(this.type).label; } get requiredErrorText(): string { - return this.requiredText || this.firmwareTypeTranslation.get(this.type).required; + return this.requiredText || OtaUpdateTranslation.get(this.type).required; + } + + get notFoundPackage(): string { + return OtaUpdateTranslation.get(this.type).noFound; } - get notFoundFirmware(): string { - return this.firmwareTypeTranslation.get(this.type).noFound; + get notMatchingPackage(): string { + return OtaUpdateTranslation.get(this.type).noMatching; } - get notMatchingFirmware(): string { - return this.firmwareTypeTranslation.get(this.type).noMatching; + get hintText(): string { + return OtaUpdateTranslation.get(this.type).hint; } - firmwareTitleText(firmware: FirmwareInfo): string { - return `${firmware.title} (${firmware.version})`; + packageTitleText(firpackageInfomware: OtaPackageInfo): string { + return `${firpackageInfomware.title} (${firpackageInfomware.version})`; } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 44327e1c2c..b72e2b196d 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -121,6 +121,7 @@ export const HelpLinks = { entitiesImport: helpBaseUrl + '/docs/user-guide/bulk-provisioning', rulechains: helpBaseUrl + '/docs/user-guide/ui/rule-chains', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', + otaUpdates: helpBaseUrl + '/docs/user-guide/ui/ota-updates', widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', widgetsConfig: helpBaseUrl + '/docs/user-guide/ui/dashboards#widget-configuration', widgetsConfigTimeseries: helpBaseUrl + '/docs/user-guide/ui/dashboards#timeseries', diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index fe7868d584..9b13c4dc72 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -27,7 +27,7 @@ import { KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; import * as _moment from 'moment'; import { AbstractControl, ValidationErrors } from '@angular/forms'; -import { FirmwareId } from '@shared/models/id/firmware-id'; +import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; export enum DeviceProfileType { @@ -500,8 +500,8 @@ export interface DeviceProfile extends BaseData { defaultRuleChainId?: RuleChainId; defaultDashboardId?: DashboardId; defaultQueueName?: string; - firmwareId?: FirmwareId; - softwareId?: FirmwareId; + firmwareId?: OtaPackageId; + softwareId?: OtaPackageId; profileData: DeviceProfileData; } @@ -563,8 +563,8 @@ export interface Device extends BaseData { name: string; type: string; label: string; - firmwareId?: FirmwareId; - softwareId?: FirmwareId; + firmwareId?: OtaPackageId; + softwareId?: OtaPackageId; deviceProfileId?: DeviceProfileId; deviceData?: DeviceData; additionalInfo?: any; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index ae2382e0ea..d088cbc88a 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -35,7 +35,7 @@ export enum EntityType { WIDGET_TYPE = 'WIDGET_TYPE', API_USAGE_STATE = 'API_USAGE_STATE', TB_RESOURCE = 'TB_RESOURCE', - FIRMWARE = 'FIRMWARE' + OTA_PACKAGE = 'OTA_PACKAGE' } export enum AliasEntityType { @@ -296,14 +296,14 @@ export const entityTypeTranslations = new Map( +export const OtaUpdateTypeTranslationMap = new Map( [ - [FirmwareType.FIRMWARE, 'firmware.types.firmware'], - [FirmwareType.SOFTWARE, 'firmware.types.software'] + [OtaUpdateType.FIRMWARE, 'ota-update.types.firmware'], + [OtaUpdateType.SOFTWARE, 'ota-update.types.software'] ] ); -export interface FirmwareInfo extends BaseData { +export interface OtaUpdateTranslation { + label: string; + required: string; + noFound: string; + noMatching: string; + hint: string; +} + +export const OtaUpdateTranslation = new Map( + [ + [OtaUpdateType.FIRMWARE, { + label: 'ota-update.assign-firmware', + required: 'ota-update.assign-firmware-required', + noFound: 'ota-update.no-firmware-text', + noMatching: 'ota-update.no-firmware-matching', + hint: 'ota-update.chose-firmware-distributed-device' + }], + [OtaUpdateType.SOFTWARE, { + label: 'ota-update.assign-software', + required: 'ota-update.assign-software-required', + noFound: 'ota-update.no-software-text', + noMatching: 'ota-update.no-software-matching', + hint: 'ota-update.chose-software-distributed-device' + }] + ] +); + +export interface OtaPackageInfo extends BaseData { tenantId?: TenantId; - type: FirmwareType; + type: OtaUpdateType; deviceProfileId?: DeviceProfileId; title?: string; version?: string; @@ -68,7 +95,7 @@ export interface FirmwareInfo extends BaseData { additionalInfo?: any; } -export interface Firmware extends FirmwareInfo { +export interface OtaPackage extends OtaPackageInfo { file?: File; data: string; } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 925b297e77..5e71d4c44a 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -141,7 +141,7 @@ import { FileSizePipe } from '@shared/pipe/file-size.pipe'; import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component'; import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; -import { FirmwareAutocompleteComponent } from '@shared/components/firmware/firmware-autocomplete.component'; +import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ota-package-autocomplete.component'; import { MAT_DATE_LOCALE } from '@angular/material/core'; @NgModule({ @@ -239,7 +239,7 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; HistorySelectorComponent, EntityGatewaySelectComponent, ContactComponent, - FirmwareAutocompleteComponent, + OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent ], imports: [ @@ -411,7 +411,7 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; HistorySelectorComponent, EntityGatewaySelectComponent, ContactComponent, - FirmwareAutocompleteComponent, + OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent ] }) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c04da7b2e7..204abf4f8c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1516,7 +1516,7 @@ "list-of-edges": "{ count, plural, 1 {One edge} other {List of # edges} }", "edge-name-starts-with": "Edges whose names start with '{{prefix}}'", "type-tb-resource": "Resource", - "type-firmware": "Firmware" + "type-ota-package": "OTA package" }, "entity-field": { "created-time": "Created time", @@ -1927,51 +1927,6 @@ "inherit-owner": "Inherit from owner", "source-attribute-not-set": "If source attribute isn't set" }, - "firmware": { - "add": "Add firmware", - "checksum": "Checksum", - "checksum-copied-message": "Firmware checksum has been copied to clipboard", - "checksum-required": "Checksum is required.", - "checksum-algorithm": "Checksum algorithm", - "content-type": "Content type", - "copy-checksum": "Copy checksum", - "copyId": "Copy firmware Id", - "description": "Description", - "delete": "Delete firmware", - "delete-firmware-text": "Be careful, after the confirmation the firmware will become unrecoverable.", - "delete-firmware-title": "Are you sure you want to delete the firmware '{{firmwareTitle}}'?", - "delete-firmwares-action-title": "Delete { count, plural, 1 {1 firmware} other {# firmwares} }", - "delete-firmwares-text": "Be careful, after the confirmation all selected resources will be removed.", - "delete-firmwares-title": "Are you sure you want to delete { count, plural, 1 {1 firmware} other {# firmwares} }?", - "download": "Download firmware", - "drop-file": "Drop a firmware file or click to select a file to upload.", - "empty": "Firmware is empty", - "idCopiedMessage": "Firmware Id has been copied to clipboard", - "no-firmware-matching": "No firmware matching '{{entity}}' were found.", - "no-firmware-text": "No firmwares found", - "no-software-matching": "No sowtware matching '{{entity}}' were found.", - "no-software-text": "No software found", - "file-name": "File name", - "file-size": "File size", - "file-size-bytes": "File size in bytes", - "firmware": "Firmware", - "firmware-details": "Firmware details", - "firmware-required": "Firmware is required.", - "search": "Search firmwares", - "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected", - "software": "Software", - "software-required": "Software is required.", - "title": "Title", - "title-required": "Title is required.", - "type": "Firmware type", - "types": { - "firmware": "Firmware", - "software": "Software" - }, - "version": "Version", - "version-required": "Version is required.", - "warning-after-save-no-edit": "Once the firmware is saved, it will not be possible to change the title and version fields." - }, "fullscreen": { "expand": "Expand to fullscreen", "exit": "Exit fullscreen", @@ -2191,6 +2146,55 @@ "or": "or", "error": "Login error" }, + "ota-update": { + "add": "Add package", + "assign-firmware": "Assigned firmware", + "assign-firmware-required": "Assigned firmware is required", + "assign-software": "Assigned software", + "assign-software-required": "Assigned software is required", + "checksum": "Checksum", + "checksum-algorithm": "Checksum algorithm", + "checksum-copied-message": "Package checksum has been copied to clipboard", + "chose-compatible-device-profile": "Choose compatible device profile", + "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", + "chose-software-distributed-device": "Choose software that will be distributed to the devices", + "content-type": "Content type", + "copy-checksum": "Copy checksum", + "copyId": "Copy package Id", + "idCopiedMessage": "Package Id has been copied to clipboard", + "description": "Description", + "delete": "Delete package", + "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", + "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", + "delete-ota-updates-text": "Be careful, after the confirmation all selected OTA updates will be removed.", + "delete-ota-updates-title": "Are you sure you want to delete { count, plural, 1 {1 OTA update} other {# OTA updates} }?", + "drop-file": "Drop a package file or click to select a file to upload.", + "download": "Download package", + "file-name": "File name", + "file-size": "File size", + "file-size-bytes": "File size in bytes", + "no-packages-text": "No packages found", + "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "no-firmware-matching": "No compatible Firmware OTA Update packages matching '{{entity}}' were found.", + "no-software-text": "No compatible Software OTA Update packages provisioned.", + "no-software-matching": "No compatible Software OTA Update packages matching '{{entity}}' were found.", + "search": "Search packages", + "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", + "ota-update": "OTA update", + "ota-update-details": "OTA update details", + "ota-updates": "OTA updates", + "package-type": "Package type", + "packages-repository": "Packages repository", + "title": "Title", + "title-required": "Title is required.", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "version": "Version", + "version-required": "Version is required.", + "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." + }, "position": { "top": "Top", "bottom": "Bottom", From 161c3ad40e63ab76f901f5fee75cdbc9dca42181 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 31 May 2021 19:50:39 +0300 Subject: [PATCH 11/75] firmware dashboard improvements --- .../data/json/demo/dashboards/firmware.json | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/application/src/main/data/json/demo/dashboards/firmware.json b/application/src/main/data/json/demo/dashboards/firmware.json index 6e8d18cd98..4711e34702 100644 --- a/application/src/main/data/json/demo/dashboards/firmware.json +++ b/application/src/main/data/json/demo/dashboards/firmware.json @@ -1,5 +1,6 @@ { "title": "Firmware", + "image": null, "configuration": { "description": "", "widgets": { @@ -247,7 +248,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -257,7 +258,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1021,7 +1022,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1031,7 +1032,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1297,7 +1298,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1307,7 +1308,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1573,7 +1574,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1583,7 +1584,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1849,7 +1850,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1859,7 +1860,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { From b4ce9e15cc2127235794afd3efaf6d0e007a4a2f Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 1 Jun 2021 10:52:28 +0300 Subject: [PATCH 12/75] remove transaction from TbLwM2mRedisRegistrationStore --- .../lwm2m/server/DefaultLwM2MTransportMsgHandler.java | 1 - .../server/store/TbLwM2mRedisRegistrationStore.java | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index abe2a50855..2ae1be66f3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -247,7 +247,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { - log.error("Client unRegistration -> test", new RuntimeException()); unRegistrationExecutor.submit(() -> { try { this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java index 973124b4e9..365b92bf66 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java @@ -337,23 +337,24 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto } } + //TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies private void removeAddrIndex(RedisConnection connection, Registration registration) { // Watch the key to remove. byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress()); - connection.watch(regAddrKey); +// connection.watch(regAddrKey); byte[] epFromAddr = connection.get(regAddrKey); // Delete the key if needed. if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) { // Try to delete the key - connection.multi(); +// connection.multi(); connection.del(regAddrKey); - connection.exec(); +// connection.exec(); // if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to // delete it anymore. } else { // the key must not be deleted. - connection.unwatch(); +// connection.unwatch(); } } From 77c02523f44c94b6e708fba31eef7695d5991755 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 1 Jun 2021 11:36:25 +0300 Subject: [PATCH 13/75] UI: Change order translate ota-package --- .../src/assets/locale/locale.constant-en_US.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 204abf4f8c..6966888ea3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2161,30 +2161,30 @@ "content-type": "Content type", "copy-checksum": "Copy checksum", "copyId": "Copy package Id", - "idCopiedMessage": "Package Id has been copied to clipboard", - "description": "Description", "delete": "Delete package", "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", "delete-ota-updates-text": "Be careful, after the confirmation all selected OTA updates will be removed.", "delete-ota-updates-title": "Are you sure you want to delete { count, plural, 1 {1 OTA update} other {# OTA updates} }?", - "drop-file": "Drop a package file or click to select a file to upload.", + "description": "Description", "download": "Download package", + "drop-file": "Drop a package file or click to select a file to upload.", "file-name": "File name", "file-size": "File size", "file-size-bytes": "File size in bytes", - "no-packages-text": "No packages found", - "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "idCopiedMessage": "Package Id has been copied to clipboard", "no-firmware-matching": "No compatible Firmware OTA Update packages matching '{{entity}}' were found.", - "no-software-text": "No compatible Software OTA Update packages provisioned.", + "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "no-packages-text": "No packages found", "no-software-matching": "No compatible Software OTA Update packages matching '{{entity}}' were found.", - "search": "Search packages", - "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", + "no-software-text": "No compatible Software OTA Update packages provisioned.", "ota-update": "OTA update", "ota-update-details": "OTA update details", "ota-updates": "OTA updates", "package-type": "Package type", "packages-repository": "Packages repository", + "search": "Search packages", + "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", "title": "Title", "title-required": "Title is required.", "types": { From abf8ff25b5133af0377a44408a770bbe24826ba3 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Tue, 1 Jun 2021 12:12:35 +0300 Subject: [PATCH 14/75] Do not create alarm state if alarms creation is disabled --- .../exception/ApiUsageLimitsExceededException.java | 10 ++++++++++ .../thingsboard/server/dao/alarm/BaseAlarmService.java | 3 ++- .../thingsboard/rule/engine/profile/DeviceState.java | 8 +++++++- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java new file mode 100644 index 0000000000..84a3dba658 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java @@ -0,0 +1,10 @@ +package org.thingsboard.server.common.data.exception; + +public class ApiUsageLimitsExceededException extends RuntimeException { + public ApiUsageLimitsExceededException(String message) { + super(message); + } + + public ApiUsageLimitsExceededException() { + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java index bb54efe769..90d988c20b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java @@ -34,6 +34,7 @@ import org.thingsboard.server.common.data.alarm.AlarmQuery; import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; +import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -119,7 +120,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ Alarm existing = alarmDao.findLatestByOriginatorAndType(alarm.getTenantId(), alarm.getOriginator(), alarm.getType()).get(); if (existing == null || existing.getStatus().isCleared()) { if (!alarmCreationEnabled) { - throw new IllegalStateException("Alarm creation is disabled"); + throw new ApiUsageLimitsExceededException("Alarms creation is disabled"); } return createAlarm(alarm); } else { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java index 69614f8079..6b7a695eea 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/profile/DeviceState.java @@ -29,6 +29,7 @@ import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.device.profile.AlarmConditionFilterKey; import org.thingsboard.server.common.data.device.profile.AlarmConditionKeyType; import org.thingsboard.server.common.data.device.profile.DeviceProfileAlarm; +import org.thingsboard.server.common.data.exception.ApiUsageLimitsExceededException; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EntityId; @@ -261,7 +262,12 @@ class DeviceState { for (DeviceProfileAlarm alarm : deviceProfile.getAlarmSettings()) { AlarmState alarmState = alarmStates.computeIfAbsent(alarm.getId(), a -> new AlarmState(this.deviceProfile, deviceId, alarm, getOrInitPersistedAlarmState(alarm), dynamicPredicateValueCtx)); - stateChanged |= alarmState.process(ctx, msg, latestValues, update); + try { + stateChanged |= alarmState.process(ctx, msg, latestValues, update); + } catch (ApiUsageLimitsExceededException e) { + alarmStates.remove(alarm.getId()); + throw e; + } } } } From 2aba74a3131a8b5fece67be959d9cb2b7bd25e24 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Tue, 1 Jun 2021 15:51:26 +0300 Subject: [PATCH 15/75] LWM2M: add AtomicInteger ts --- .../lwm2m/server/LwM2mTransportServerHelper.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java index fff35ea855..d31883015e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServerHelper.java @@ -53,6 +53,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V; @@ -64,6 +66,13 @@ public class LwM2mTransportServerHelper { private final LwM2mTransportContext context; private final LwM2MJsonAdaptor adaptor; + private final AtomicInteger atomicTs = new AtomicInteger(0); + + + public long getTS() { + int addTs = atomicTs.getAndIncrement() >= 1000 ? atomicTs.getAndSet(0) : atomicTs.get(); + return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + addTs; + } /** * send to Thingsboard Attribute || Telemetry @@ -96,7 +105,7 @@ public class LwM2mTransportServerHelper { public void sendParametersOnThingsboardTelemetry(List result, SessionInfoProto sessionInfo) { PostTelemetryMsg.Builder request = PostTelemetryMsg.newBuilder(); TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder(); - builder.setTs(System.currentTimeMillis()); + builder.setTs(this.getTS()); builder.addAllKv(result); request.addTsKvList(builder.build()); PostTelemetryMsg postTelemetryMsg = request.build(); From 1bcee16fa3b13894477ee7f970f5b6a636e45845 Mon Sep 17 00:00:00 2001 From: Viacheslav Klimov Date: Wed, 2 Jun 2021 09:21:14 +0300 Subject: [PATCH 16/75] License headers --- .../ApiUsageLimitsExceededException.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java index 84a3dba658..fb5c4dffeb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/exception/ApiUsageLimitsExceededException.java @@ -1,3 +1,18 @@ +/** + * Copyright © 2016-2021 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.exception; public class ApiUsageLimitsExceededException extends RuntimeException { From 67de61e6d5094de6a986e8b173f03f141973fbb5 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 2 Jun 2021 11:40:20 +0300 Subject: [PATCH 17/75] fixed OtaPackage data cache --- .../server/controller/DeviceController.java | 8 ++-- .../src/main/resources/thingsboard.yml | 3 ++ .../cache/ota/CaffeineOtaPackageCache.java | 7 +-- .../cache/ota/RedisOtaPackageDataCache.java | 3 +- .../server/common/data/CacheConstants.java | 1 + .../server/dao/ota/BaseOtaPackageService.java | 15 ++----- .../service/BaseOtaPackageServiceTest.java | 44 +------------------ .../resources/application-test.properties | 3 ++ 8 files changed, 23 insertions(+), 61 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 90094e88c1..b13393515c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -782,15 +782,15 @@ public class DeviceController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/devices/count", method = RequestMethod.GET) + @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET) @ResponseBody - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@RequestParam(required = false) String otaPackageType, - @RequestParam(required = false) String deviceProfileId) throws ThingsboardException { + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType, + @RequestParam String deviceProfileId) throws ThingsboardException { checkParameter("OtaPackageType", otaPackageType); checkParameter("DeviceProfileId", deviceProfileId); try { return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(deviceProfileId)); + getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType)); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ae628bf9f6..de1f3cd3af 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -374,6 +374,9 @@ caffeine: otaPackages: timeToLiveInMinutes: 60 maxSize: 10 + otaPackagesData: + timeToLiveInMinutes: 60 + maxSize: 10 edges: timeToLiveInMinutes: 1440 maxSize: 0 diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java index a864fc6dba..8b1f0804f3 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java @@ -21,6 +21,7 @@ import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @@ -36,7 +37,7 @@ public class CaffeineOtaPackageCache implements OtaPackageDataCache { @Override public byte[] get(String key, int chunkSize, int chunk) { - byte[] data = cacheManager.getCache(OTA_PACKAGE_CACHE).get(key, byte[].class); + byte[] data = cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).get(key, byte[].class); if (chunkSize < 1) { return data; @@ -58,11 +59,11 @@ public class CaffeineOtaPackageCache implements OtaPackageDataCache { @Override public void put(String key, byte[] value) { - cacheManager.getCache(OTA_PACKAGE_CACHE).putIfAbsent(key, value); + cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).putIfAbsent(key, value); } @Override public void evict(String key) { - cacheManager.getCache(OTA_PACKAGE_CACHE).evict(key); + cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).evict(key); } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java index 1e4ae53829..c2c3bc34a8 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java @@ -22,6 +22,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.stereotype.Service; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @@ -63,6 +64,6 @@ public class RedisOtaPackageDataCache implements OtaPackageDataCache { } private byte[] toOtaPackageCacheKey(String key) { - return String.format("%s::%s", OTA_PACKAGE_CACHE, key).getBytes(); + return String.format("%s::%s", OTA_PACKAGE_DATA_CACHE, key).getBytes(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index 3cc3f56737..ced7a64a0f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -30,4 +30,5 @@ public class CacheConstants { public static final String ATTRIBUTES_CACHE = "attributes"; public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime"; public static final String OTA_PACKAGE_CACHE = "otaPackages"; + public static final String OTA_PACKAGE_DATA_CACHE = "otaPackagesData"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index ae19576861..536c79843a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -221,8 +221,6 @@ public class BaseOtaPackageService implements OtaPackageService { @Override protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); - - validateUpdateDeviceProfile(otaPackage, otaPackageOld); BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); } }; @@ -261,7 +259,6 @@ public class BaseOtaPackageService implements OtaPackageService { protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); - validateUpdateDeviceProfile(otaPackage, otaPackageOld); BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { @@ -270,14 +267,6 @@ public class BaseOtaPackageService implements OtaPackageService { } }; - private void validateUpdateDeviceProfile(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { - if (otaPackageOld.getDeviceProfileId() != null && !otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { - if (otaPackageInfoDao.isOtaPackageUsed(otaPackageOld.getId(), otaPackage.getType(), otaPackageOld.getDeviceProfileId())) { - throw new DataValidationException("Can`t update deviceProfileId because otaPackage is already in use!"); - } - } - } - private static void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { if (!otaPackageOld.getType().equals(otaPackage.getType())) { throw new DataValidationException("Updating type is prohibited!"); @@ -291,6 +280,10 @@ public class BaseOtaPackageService implements OtaPackageService { throw new DataValidationException("Updating otaPackage version is prohibited!"); } + if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { + throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); + } + if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { throw new DataValidationException("Updating otaPackage file name is prohibited!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index e7cee8c878..ab895e1052 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -408,7 +408,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { } @Test - public void testUpdateDeviceProfileIdWithReferenceByDevice() { + public void testUpdateDeviceProfileId() { OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); @@ -422,20 +422,12 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setData(DATA); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Device device = new Device(); - device.setTenantId(tenantId); - device.setName("My device"); - device.setDeviceProfileId(deviceProfileId); - device.setFirmwareId(savedFirmware.getId()); - Device savedDevice = deviceService.saveDevice(device); - try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); + thrown.expectMessage("Updating otaPackage deviceProfile is prohibited!"); savedFirmware.setDeviceProfileId(null); otaPackageService.saveOtaPackage(savedFirmware); } finally { - deviceService.deleteDevice(tenantId, savedDevice.getId()); otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -471,38 +463,6 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { } } - @Test - public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() { - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); - DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(savedDeviceProfile.getId()); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - - savedDeviceProfile.setFirmwareId(savedFirmware.getId()); - deviceProfileService.saveDeviceProfile(savedDeviceProfile); - - try { - thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); - savedFirmware.setDeviceProfileId(null); - otaPackageService.saveOtaPackage(savedFirmware); - } finally { - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); - } - } - @Test public void testFindFirmwareById() { OtaPackage firmware = new OtaPackage(); diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 74eb4f43f0..d7a960471b 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -39,6 +39,9 @@ caffeine.specs.deviceProfiles.maxSize=100000 caffeine.specs.otaPackages.timeToLiveInMinutes=1440 caffeine.specs.otaPackages.maxSize=100000 +caffeine.specs.otaPackagesData.timeToLiveInMinutes=1440 +caffeine.specs.otaPackagesData.maxSize=100000 + caffeine.specs.edges.timeToLiveInMinutes=1440 caffeine.specs.edges.maxSize=100000 From a315c85a3b915f0009e03dc51e8a839376160f9b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 11:42:56 +0300 Subject: [PATCH 18/75] UI: Rename 'Add credential' to 'Add credentials' --- .../home/components/wizard/device-wizard-dialog.component.html | 2 +- ui-ngx/src/assets/locale/locale.constant-cs_CZ.json | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- ui-ngx/src/assets/locale/locale.constant-es_ES.json | 2 +- ui-ngx/src/assets/locale/locale.constant-ko_KR.json | 2 +- ui-ngx/src/assets/locale/locale.constant-sl_SI.json | 2 +- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 245cb6503b..39be34b823 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -155,7 +155,7 @@ {{ 'device.credentials' | translate }}
- {{ 'device.wizard.add-credential' | translate }} + {{ 'device.wizard.add-credentials' | translate }} diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 92941c9404..128e8d6d93 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -923,7 +923,7 @@ "existing-device-profile": "Vybrat existující profil zařízení", "specific-configuration": "Specifická konfigurace", "customer-to-assign-device": "Přiřadit zařízení zákazníkovi", - "add-credential": "Přidat přístupový údaj" + "add-credentials": "Přidat přístupový údaj" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c04da7b2e7..c5a40829b6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1000,7 +1000,7 @@ "existing-device-profile": "Select existing device profile", "specific-configuration": "Specific configuration", "customer-to-assign-device": "Customer to assign the device", - "add-credential": "Add credential" + "add-credentials": "Add credentials" }, "unassign-devices-from-edge-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?", "unassign-devices-from-edge-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the edge." diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 9d18102a43..fc1049ca91 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -947,7 +947,7 @@ "existing-device-profile": "Seleccionar un perfil existente", "specific-configuration": "Configuración específica", "customer-to-assign-device": "Cliente al que asignar el dispositivo", - "add-credential": "Añadir credencial" + "add-credentials": "Añadir credencial" }, "assign-device-to-edge-text": "Seleccione los dispositivos para asignar al borde", "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index cbe962bc2a..f966203fbf 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -920,7 +920,7 @@ "existing-device-profile": "기존 장치 프로파일 선택", "specific-configuration": "특수 설정", "customer-to-assign-device": "장치에 할당할 커스터머", - "add-credential": "크리덴셜 추가" + "add-credentials": "크리덴셜 추가" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 7dd60aaa2f..e57ee78e87 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -920,7 +920,7 @@ "existing-device-profile": "Select existing device profile", "specific-configuration": "Specific configuration", "customer-to-assign-device": "Customer to assign the device", - "add-credential": "Add credential" + "add-credentials": "Add credentials" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index b7a3078c3b..99b0864d36 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -1079,7 +1079,7 @@ "view-credentials": "查看凭据", "view-devices": "查看设备", "wizard": { - "add-credential": "添加凭据", + "add-credentials": "添加凭据", "customer-to-assign-device": "客户分配设备", "device-details": "设备详细信息", "device-wizard": "设备向导", From 139af45fd38a395d473ccc7532a4d0d3a281f6b6 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 31 May 2021 16:43:33 +0300 Subject: [PATCH 19/75] Renamed Firmware to OtaPackage --- .../main/data/upgrade/3.2.2/schema_update.sql | 14 +- .../server/controller/BaseController.java | 42 +- .../server/controller/DeviceController.java | 19 +- .../controller/DeviceProfileController.java | 2 +- .../server/controller/FirmwareController.java | 222 ---------- .../controller/OtaPackageController.java | 222 ++++++++++ .../DefaultOtaPackageStateService.java} | 136 +++---- .../OtaPackageStateService.java} | 8 +- .../queue/DefaultTbCoreConsumerService.java | 31 +- .../service/security/AccessValidator.java | 24 +- .../service/security/permission/Resource.java | 2 +- .../permission/TenantAdminPermissions.java | 2 +- .../transport/DefaultTransportApiService.java | 68 ++-- .../src/main/resources/thingsboard.yml | 16 +- ...java => BaseOtaPackageControllerTest.java} | 115 +++--- ....java => OtaPackageControllerSqlTest.java} | 4 +- .../CaffeineOtaPackageCache.java} | 12 +- .../OtaPackageDataCache.java} | 8 +- .../RedisOtaPackageDataCache.java} | 18 +- .../server/dao/device/DeviceService.java | 5 +- .../server/dao/firmware/FirmwareService.java | 52 --- .../server/dao/ota/OtaPackageService.java | 52 +++ .../server/common/data/CacheConstants.java | 2 +- .../server/common/data/Device.java | 16 +- .../server/common/data/DeviceProfile.java | 8 +- .../server/common/data/EntityType.java | 2 +- .../{HasFirmware.java => HasOtaPackage.java} | 8 +- .../data/{Firmware.java => OtaPackage.java} | 10 +- ...{FirmwareInfo.java => OtaPackageInfo.java} | 40 +- .../common/data/id/EntityIdFactory.java | 4 +- .../id/{FirmwareId.java => OtaPackageId.java} | 10 +- .../{firmware => ota}/ChecksumAlgorithm.java | 2 +- .../OtaPackageKey.java} | 6 +- .../OtaPackageType.java} | 6 +- .../OtaPackageUpdateStatus.java} | 4 +- .../OtaPackageUtil.java} | 49 ++- .../queue/kafka/TbKafkaTopicConfigs.java | 2 +- .../provider/AwsSqsMonolithQueueFactory.java | 10 +- .../provider/AwsSqsTbCoreQueueFactory.java | 12 +- .../InMemoryMonolithQueueFactory.java | 8 +- .../provider/KafkaMonolithQueueFactory.java | 22 +- .../provider/KafkaTbCoreQueueFactory.java | 22 +- .../provider/PubSubMonolithQueueFactory.java | 12 +- .../provider/PubSubTbCoreQueueFactory.java | 12 +- .../RabbitMqMonolithQueueFactory.java | 12 +- .../provider/RabbitMqTbCoreQueueFactory.java | 12 +- .../ServiceBusMonolithQueueFactory.java | 12 +- .../ServiceBusTbCoreQueueFactory.java | 12 +- .../queue/provider/TbCoreQueueFactory.java | 6 +- .../provider/TbCoreQueueProducerProvider.java | 4 +- .../queue/settings/TbQueueCoreSettings.java | 4 +- common/queue/src/main/proto/queue.proto | 18 +- .../transport/coap/CoapTransportResource.java | 22 +- .../transport/http/DeviceApiController.java | 32 +- .../DefaultLwM2MTransportMsgHandler.java | 52 +-- .../lwm2m/server/LwM2mTransportRequest.java | 4 +- .../lwm2m/server/LwM2mTransportUtil.java | 48 +-- .../lwm2m/server/client/LwM2mClient.java | 6 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 28 +- .../transport/mqtt/MqttTransportHandler.java | 58 +-- .../mqtt/adaptors/JsonMqttAdaptor.java | 4 +- .../mqtt/adaptors/MqttTransportAdaptor.java | 4 +- .../mqtt/adaptors/ProtoMqttAdaptor.java | 4 +- .../common/transport/TransportContext.java | 5 +- .../common/transport/TransportService.java | 6 +- .../service/DefaultTransportService.java | 6 +- .../server/dao/device/DeviceDao.java | 8 +- .../dao/device/DeviceProfileServiceImpl.java | 16 +- .../server/dao/device/DeviceServiceImpl.java | 45 ++- .../server/dao/entity/BaseEntityService.java | 12 +- .../dao/firmware/BaseFirmwareService.java | 379 ------------------ .../server/dao/model/ModelConstants.java | 31 +- .../dao/model/sql/AbstractDeviceEntity.java | 6 +- .../dao/model/sql/DeviceProfileEntity.java | 6 +- ...mwareEntity.java => OtaPackageEntity.java} | 70 ++-- ...oEntity.java => OtaPackageInfoEntity.java} | 72 ++-- .../server/dao/ota/BaseOtaPackageService.java | 373 +++++++++++++++++ .../OtaPackageDao.java} | 6 +- .../OtaPackageInfoDao.java} | 18 +- .../dao/sql/device/DeviceRepository.java | 20 +- .../server/dao/sql/device/JpaDeviceDao.java | 36 +- .../sql/firmware/FirmwareInfoRepository.java | 60 --- .../dao/sql/firmware/JpaFirmwareInfoDao.java | 94 ----- .../JpaOtaPackageDao.java} | 20 +- .../dao/sql/ota/JpaOtaPackageInfoDao.java | 94 +++++ .../dao/sql/ota/OtaPackageInfoRepository.java | 60 +++ .../OtaPackageRepository.java} | 6 +- .../server/dao/tenant/TenantServiceImpl.java | 6 +- .../resources/sql/schema-entities-hsql.sql | 14 +- .../main/resources/sql/schema-entities.sql | 14 +- .../dao/service/AbstractServiceTest.java | 4 +- .../service/BaseDeviceProfileServiceTest.java | 11 +- .../dao/service/BaseDeviceServiceTest.java | 14 +- ...st.java => BaseOtaPackageServiceTest.java} | 214 +++++----- ...est.java => OtaPackageServiceSqlTest.java} | 4 +- .../resources/application-test.properties | 4 +- .../resources/sql/hsql/drop-all-tables.sql | 2 +- 97 files changed, 1722 insertions(+), 1697 deletions(-) delete mode 100644 application/src/main/java/org/thingsboard/server/controller/FirmwareController.java create mode 100644 application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java rename application/src/main/java/org/thingsboard/server/service/{firmware/DefaultFirmwareStateService.java => ota/DefaultOtaPackageStateService.java} (69%) rename application/src/main/java/org/thingsboard/server/service/{firmware/FirmwareStateService.java => ota/OtaPackageStateService.java} (79%) rename application/src/test/java/org/thingsboard/server/controller/{BaseFirmwareControllerTest.java => BaseOtaPackageControllerTest.java} (66%) rename application/src/test/java/org/thingsboard/server/controller/sql/{FirmwareControllerSqlTest.java => OtaPackageControllerSqlTest.java} (82%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/CaffeineFirmwareCache.java => ota/CaffeineOtaPackageCache.java} (84%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/FirmwareDataCache.java => ota/OtaPackageDataCache.java} (82%) rename common/cache/src/main/java/org/thingsboard/server/cache/{firmware/RedisFirmwareDataCache.java => ota/RedisOtaPackageDataCache.java} (78%) delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java rename common/data/src/main/java/org/thingsboard/server/common/data/{HasFirmware.java => HasOtaPackage.java} (80%) rename common/data/src/main/java/org/thingsboard/server/common/data/{Firmware.java => OtaPackage.java} (82%) rename common/data/src/main/java/org/thingsboard/server/common/data/{FirmwareInfo.java => OtaPackageInfo.java} (58%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{FirmwareId.java => OtaPackageId.java} (79%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware => ota}/ChecksumAlgorithm.java (93%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareKey.java => ota/OtaPackageKey.java} (88%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareType.java => ota/OtaPackageType.java} (86%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareUpdateStatus.java => ota/OtaPackageUpdateStatus.java} (88%) rename common/data/src/main/java/org/thingsboard/server/common/data/{firmware/FirmwareUtil.java => ota/OtaPackageUtil.java} (52%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{FirmwareEntity.java => OtaPackageEntity.java} (62%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{FirmwareInfoEntity.java => OtaPackageInfoEntity.java} (63%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java rename dao/src/main/java/org/thingsboard/server/dao/{firmware/FirmwareDao.java => ota/OtaPackageDao.java} (81%) rename dao/src/main/java/org/thingsboard/server/dao/{firmware/FirmwareInfoDao.java => ota/OtaPackageInfoDao.java} (55%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java rename dao/src/main/java/org/thingsboard/server/dao/sql/{firmware/JpaFirmwareDao.java => ota/JpaOtaPackageDao.java} (62%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/{firmware/FirmwareRepository.java => ota/OtaPackageRepository.java} (78%) rename dao/src/test/java/org/thingsboard/server/dao/service/{BaseFirmwareServiceTest.java => BaseOtaPackageServiceTest.java} (74%) rename dao/src/test/java/org/thingsboard/server/dao/service/sql/{FirmwareServiceSqlTest.java => OtaPackageServiceSqlTest.java} (83%) diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 4fec49130a..2814e18c2f 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -59,8 +59,8 @@ CREATE TABLE IF NOT EXISTS resource ( CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_key) ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid, @@ -75,7 +75,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); ALTER TABLE dashboard @@ -101,13 +101,13 @@ DO $$ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device_profile') THEN ALTER TABLE device_profile ADD CONSTRAINT fk_firmware_device_profile - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN ALTER TABLE device_profile ADD CONSTRAINT fk_software_device_profile - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_default_dashboard_device_profile') THEN @@ -119,13 +119,13 @@ DO $$ IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN ALTER TABLE device ADD CONSTRAINT fk_firmware_device - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN ALTER TABLE device ADD CONSTRAINT fk_software_device - FOREIGN KEY (firmware_id) REFERENCES firmware(id); + FOREIGN KEY (firmware_id) REFERENCES ota_package(id); END IF; END; $$; diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index e6384c010d..dd1d388a08 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -39,8 +39,8 @@ import org.thingsboard.server.common.data.EdgeUtils; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.TbResourceInfo; @@ -70,7 +70,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; @@ -110,7 +110,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2Service; @@ -128,7 +128,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.component.ComponentDiscoveryService; -import org.thingsboard.server.service.firmware.FirmwareStateService; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; import org.thingsboard.server.service.edge.rpc.EdgeGrpcService; import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; @@ -250,10 +250,10 @@ public abstract class BaseController { protected TbResourceService resourceService; @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; @Autowired - protected FirmwareStateService firmwareStateService; + protected OtaPackageStateService otaPackageStateService; @Autowired protected TbQueueProducerProvider producerProvider; @@ -511,8 +511,8 @@ public abstract class BaseController { case TB_RESOURCE: checkResourceId(new TbResourceId(entityId.getId()), operation); return; - case FIRMWARE: - checkFirmwareId(new FirmwareId(entityId.getId()), operation); + case OTA_PACKAGE: + checkOtaPackageId(new OtaPackageId(entityId.getId()), operation); return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); @@ -769,25 +769,25 @@ public abstract class BaseController { } } - Firmware checkFirmwareId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { + OtaPackage checkOtaPackageId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { try { - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); - Firmware firmware = firmwareService.findFirmwareById(getCurrentUser().getTenantId(), firmwareId); - checkNotNull(firmware); - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmware); - return firmware; + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); + OtaPackage otaPackage = otaPackageService.findOtaPackageById(getCurrentUser().getTenantId(), otaPackageId); + checkNotNull(otaPackage); + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackage); + return otaPackage; } catch (Exception e) { throw handleException(e, false); } } - FirmwareInfo checkFirmwareInfoId(FirmwareId firmwareId, Operation operation) throws ThingsboardException { + OtaPackageInfo checkOtaPackageInfoId(OtaPackageId otaPackageId, Operation operation) throws ThingsboardException { try { - validateId(firmwareId, "Incorrect firmwareId " + firmwareId); - FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(getCurrentUser().getTenantId(), firmwareId); - checkNotNull(firmwareInfo); - accessControlService.checkPermission(getCurrentUser(), Resource.FIRMWARE, operation, firmwareId, firmwareInfo); - return firmwareInfo; + validateId(otaPackageId, "Incorrect otaPackageId " + otaPackageId); + OtaPackageInfo otaPackageIn = otaPackageService.findOtaPackageInfoById(getCurrentUser().getTenantId(), otaPackageId); + checkNotNull(otaPackageIn); + accessControlService.checkPermission(getCurrentUser(), Resource.OTA_PACKAGE, operation, otaPackageId, otaPackageIn); + return otaPackageIn; } catch (Exception e) { throw handleException(e, false); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 378083b873..90094e88c1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -75,6 +76,7 @@ import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; import static org.thingsboard.server.controller.EdgeController.EDGE_ID; @@ -153,7 +155,7 @@ public class DeviceController extends BaseController { deviceStateService.onDeviceUpdated(savedDevice); } - firmwareStateService.update(savedDevice, oldDevice); + otaPackageStateService.update(savedDevice, oldDevice); return savedDevice; } catch (Exception e) { @@ -778,4 +780,19 @@ public class DeviceController extends BaseController { throw handleException(e); } } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/devices/count", method = RequestMethod.GET) + @ResponseBody + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@RequestParam(required = false) String otaPackageType, + @RequestParam(required = false) String deviceProfileId) throws ThingsboardException { + checkParameter("OtaPackageType", otaPackageType); + checkParameter("DeviceProfileId", deviceProfileId); + try { + return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( + getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(deviceProfileId)); + } catch (Exception e) { + throw handleException(e); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 849bcb51b9..ceee45147b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -168,7 +168,7 @@ public class DeviceProfileController extends BaseController { null, created ? ActionType.ADDED : ActionType.UPDATED, null); - firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); + otaPackageStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); diff --git a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java b/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java deleted file mode 100644 index 6728120163..0000000000 --- a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright © 2016-2021 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.controller; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.core.io.ByteArrayResource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.multipart.MultipartFile; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.audit.ActionType; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.security.permission.Resource; - -import java.nio.ByteBuffer; - -@Slf4j -@RestController -@TbCoreComponent -@RequestMapping("/api") -public class FirmwareController extends BaseController { - - public static final String FIRMWARE_ID = "firmwareId"; - public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; - - @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}/download", method = RequestMethod.GET) - @ResponseBody - public ResponseEntity downloadFirmware(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - Firmware firmware = checkFirmwareId(firmwareId, Operation.READ); - - ByteArrayResource resource = new ByteArrayResource(firmware.getData().array()); - return ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmware.getFileName()) - .header("x-filename", firmware.getFileName()) - .contentLength(resource.contentLength()) - .contentType(parseMediaType(firmware.getContentType())) - .body(resource); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmware/info/{firmwareId}", method = RequestMethod.GET) - @ResponseBody - public FirmwareInfo getFirmwareInfoById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - return checkNotNull(firmwareService.findFirmwareInfoById(getTenantId(), firmwareId)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.GET) - @ResponseBody - public Firmware getFirmwareById(@PathVariable(FIRMWARE_ID) String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - return checkFirmwareId(firmwareId, Operation.READ); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware", method = RequestMethod.POST) - @ResponseBody - public FirmwareInfo saveFirmwareInfo(@RequestBody FirmwareInfo firmwareInfo) throws ThingsboardException { - boolean created = firmwareInfo.getId() == null; - try { - firmwareInfo.setTenantId(getTenantId()); - checkEntity(firmwareInfo.getId(), firmwareInfo, Resource.FIRMWARE); - FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); - logEntityAction(savedFirmwareInfo.getId(), savedFirmwareInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, null); - return savedFirmwareInfo; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), firmwareInfo, - null, created ? ActionType.ADDED : ActionType.UPDATED, e); - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.POST) - @ResponseBody - public Firmware saveFirmwareData(@PathVariable(FIRMWARE_ID) String strFirmwareId, - @RequestParam(required = false) String checksum, - @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, - @RequestBody MultipartFile file) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.READ); - - Firmware firmware = new Firmware(firmwareId); - firmware.setCreatedTime(info.getCreatedTime()); - firmware.setTenantId(getTenantId()); - firmware.setDeviceProfileId(info.getDeviceProfileId()); - firmware.setType(info.getType()); - firmware.setTitle(info.getTitle()); - firmware.setVersion(info.getVersion()); - firmware.setAdditionalInfo(info.getAdditionalInfo()); - - ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); - - byte[] bytes = file.getBytes(); - if (StringUtils.isEmpty(checksum)) { - checksum = firmwareService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); - } - - firmware.setChecksumAlgorithm(checksumAlgorithm); - firmware.setChecksum(checksum); - firmware.setFileName(file.getOriginalFilename()); - firmware.setContentType(file.getContentType()); - firmware.setData(ByteBuffer.wrap(bytes)); - firmware.setDataSize((long) bytes.length); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); - logEntityAction(savedFirmware.getId(), savedFirmware, null, ActionType.UPDATED, null); - return savedFirmware; - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.UPDATED, e, strFirmwareId); - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmwares", method = RequestMethod.GET) - @ResponseBody - public PageData getFirmwares(@RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(firmwareService.findTenantFirmwaresByTenantId(getTenantId(), pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) - @ResponseBody - public PageData getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId, - @PathVariable("type") String strType, - @PathVariable("hasData") boolean hasData, - @RequestParam int pageSize, - @RequestParam int page, - @RequestParam(required = false) String textSearch, - @RequestParam(required = false) String sortProperty, - @RequestParam(required = false) String sortOrder) throws ThingsboardException { - checkParameter("deviceProfileId", strDeviceProfileId); - checkParameter("type", strType); - try { - PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), - new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink)); - } catch (Exception e) { - throw handleException(e); - } - } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/firmware/{firmwareId}", method = RequestMethod.DELETE) - @ResponseBody - public void deleteFirmware(@PathVariable("firmwareId") String strFirmwareId) throws ThingsboardException { - checkParameter(FIRMWARE_ID, strFirmwareId); - try { - FirmwareId firmwareId = new FirmwareId(toUUID(strFirmwareId)); - FirmwareInfo info = checkFirmwareInfoId(firmwareId, Operation.DELETE); - firmwareService.deleteFirmware(getTenantId(), firmwareId); - logEntityAction(firmwareId, info, null, ActionType.DELETED, null, strFirmwareId); - } catch (Exception e) { - logEntityAction(emptyId(EntityType.FIRMWARE), null, null, ActionType.DELETED, e, strFirmwareId); - throw handleException(e); - } - } - -} diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java new file mode 100644 index 0000000000..02e9d4b305 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -0,0 +1,222 @@ +/** + * Copyright © 2016-2021 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.controller; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import java.nio.ByteBuffer; + +@Slf4j +@RestController +@TbCoreComponent +@RequestMapping("/api") +public class OtaPackageController extends BaseController { + + public static final String OTA_PACKAGE_ID = "otaPackageId"; + public static final String CHECKSUM_ALGORITHM = "checksumAlgorithm"; + + @PreAuthorize("hasAnyAuthority( 'TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}/download", method = RequestMethod.GET) + @ResponseBody + public ResponseEntity downloadOtaPackage(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); + + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); + return ResponseEntity.ok() + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) + .header("x-filename", otaPackage.getFileName()) + .contentLength(resource.contentLength()) + .contentType(parseMediaType(otaPackage.getContentType())) + .body(resource); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackage/info/{otaPackageId}", method = RequestMethod.GET) + @ResponseBody + public OtaPackageInfo getOtaPackageInfoById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkNotNull(otaPackageService.findOtaPackageInfoById(getTenantId(), otaPackageId)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.GET) + @ResponseBody + public OtaPackage getOtaPackageById(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + return checkOtaPackageId(otaPackageId, Operation.READ); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage", method = RequestMethod.POST) + @ResponseBody + public OtaPackageInfo saveOtaPackageInfo(@RequestBody OtaPackageInfo otaPackageInfo) throws ThingsboardException { + boolean created = otaPackageInfo.getId() == null; + try { + otaPackageInfo.setTenantId(getTenantId()); + checkEntity(otaPackageInfo.getId(), otaPackageInfo, Resource.OTA_PACKAGE); + OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(otaPackageInfo); + logEntityAction(savedOtaPackageInfo.getId(), savedOtaPackageInfo, + null, created ? ActionType.ADDED : ActionType.UPDATED, null); + return savedOtaPackageInfo; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), otaPackageInfo, + null, created ? ActionType.ADDED : ActionType.UPDATED, e); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.POST) + @ResponseBody + public OtaPackage saveOtaPackageData(@PathVariable(OTA_PACKAGE_ID) String strOtaPackageId, + @RequestParam(required = false) String checksum, + @RequestParam(CHECKSUM_ALGORITHM) String checksumAlgorithmStr, + @RequestBody MultipartFile file) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + checkParameter(CHECKSUM_ALGORITHM, checksumAlgorithmStr); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.READ); + + OtaPackage otaPackage = new OtaPackage(otaPackageId); + otaPackage.setCreatedTime(info.getCreatedTime()); + otaPackage.setTenantId(getTenantId()); + otaPackage.setDeviceProfileId(info.getDeviceProfileId()); + otaPackage.setType(info.getType()); + otaPackage.setTitle(info.getTitle()); + otaPackage.setVersion(info.getVersion()); + otaPackage.setAdditionalInfo(info.getAdditionalInfo()); + + ChecksumAlgorithm checksumAlgorithm = ChecksumAlgorithm.valueOf(checksumAlgorithmStr.toUpperCase()); + + byte[] bytes = file.getBytes(); + if (StringUtils.isEmpty(checksum)) { + checksum = otaPackageService.generateChecksum(checksumAlgorithm, ByteBuffer.wrap(bytes)); + } + + otaPackage.setChecksumAlgorithm(checksumAlgorithm); + otaPackage.setChecksum(checksum); + otaPackage.setFileName(file.getOriginalFilename()); + otaPackage.setContentType(file.getContentType()); + otaPackage.setData(ByteBuffer.wrap(bytes)); + otaPackage.setDataSize((long) bytes.length); + OtaPackage savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage); + logEntityAction(savedOtaPackage.getId(), savedOtaPackage, null, ActionType.UPDATED, null); + return savedOtaPackage; + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.UPDATED, e, strOtaPackageId); + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackages", method = RequestMethod.GET) + @ResponseBody + public PageData getOtaPackages(@RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + try { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantId(getTenantId(), pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") + @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) + @ResponseBody + public PageData getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId, + @PathVariable("type") String strType, + @PathVariable("hasData") boolean hasData, + @RequestParam int pageSize, + @RequestParam int page, + @RequestParam(required = false) String textSearch, + @RequestParam(required = false) String sortProperty, + @RequestParam(required = false) String sortOrder) throws ThingsboardException { + checkParameter("deviceProfileId", strDeviceProfileId); + checkParameter("type", strType); + try { + PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); + return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), hasData, pageLink)); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/otaPackage/{otaPackageId}", method = RequestMethod.DELETE) + @ResponseBody + public void deleteOtaPackage(@PathVariable("otaPackageId") String strOtaPackageId) throws ThingsboardException { + checkParameter(OTA_PACKAGE_ID, strOtaPackageId); + try { + OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); + OtaPackageInfo info = checkOtaPackageInfoId(otaPackageId, Operation.DELETE); + otaPackageService.deleteOtaPackage(getTenantId(), otaPackageId); + logEntityAction(otaPackageId, info, null, ActionType.DELETED, null, strOtaPackageId); + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OTA_PACKAGE), null, null, ActionType.DELETED, e, strOtaPackageId); + throw handleException(e); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java similarity index 69% rename from application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java rename to application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 9720cd2097..c5d0c0472f 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.firmware; +package org.thingsboard.server.service.ota; import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; @@ -23,12 +23,9 @@ import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKvEntry; @@ -37,13 +34,16 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; -import org.thingsboard.server.dao.firmware.FirmwareService; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.dao.ota.OtaPackageService; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; @@ -58,44 +58,43 @@ import java.util.List; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; -import java.util.function.Function; - -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey; + +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.CHECKSUM_ALGORITHM; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTargetTelemetryKey; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getTelemetryKey; @Slf4j @Service @TbCoreComponent -public class DefaultFirmwareStateService implements FirmwareStateService { +public class DefaultOtaPackageStateService implements OtaPackageStateService { private final TbClusterService tbClusterService; - private final FirmwareService firmwareService; + private final OtaPackageService otaPackageService; private final DeviceService deviceService; private final DeviceProfileService deviceProfileService; private final RuleEngineTelemetryService telemetryService; - private final TbQueueProducer> fwStateMsgProducer; + private final TbQueueProducer> otaPackageStateMsgProducer; - public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService, - DeviceService deviceService, - DeviceProfileService deviceProfileService, - RuleEngineTelemetryService telemetryService, - TbCoreQueueFactory coreQueueFactory) { + public DefaultOtaPackageStateService(TbClusterService tbClusterService, OtaPackageService otaPackageService, + DeviceService deviceService, + DeviceProfileService deviceProfileService, + RuleEngineTelemetryService telemetryService, + TbCoreQueueFactory coreQueueFactory) { this.tbClusterService = tbClusterService; - this.firmwareService = firmwareService; + this.otaPackageService = otaPackageService; this.deviceService = deviceService; this.deviceProfileService = deviceProfileService; this.telemetryService = telemetryService; - this.fwStateMsgProducer = coreQueueFactory.createToFirmwareStateServiceMsgProducer(); + this.otaPackageStateMsgProducer = coreQueueFactory.createToOtaPackageStateServiceMsgProducer(); } @Override @@ -105,14 +104,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } private void updateFirmware(Device device, Device oldDevice) { - FirmwareId newFirmwareId = device.getFirmwareId(); + OtaPackageId newFirmwareId = device.getFirmwareId(); if (newFirmwareId == null) { DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); newFirmwareId = newDeviceProfile.getFirmwareId(); } if (oldDevice != null) { if (newFirmwareId != null) { - FirmwareId oldFirmwareId = oldDevice.getFirmwareId(); + OtaPackageId oldFirmwareId = oldDevice.getFirmwareId(); if (oldFirmwareId == null) { DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); oldFirmwareId = oldDeviceProfile.getFirmwareId(); @@ -132,14 +131,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } private void updateSoftware(Device device, Device oldDevice) { - FirmwareId newSoftwareId = device.getSoftwareId(); + OtaPackageId newSoftwareId = device.getSoftwareId(); if (newSoftwareId == null) { DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); newSoftwareId = newDeviceProfile.getSoftwareId(); } if (oldDevice != null) { if (newSoftwareId != null) { - FirmwareId oldSoftwareId = oldDevice.getSoftwareId(); + OtaPackageId oldSoftwareId = oldDevice.getSoftwareId(); if (oldSoftwareId == null) { DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); oldSoftwareId = oldDeviceProfile.getSoftwareId(); @@ -170,33 +169,20 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } } - private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) { - Function> getDevicesFunction; + private void update(TenantId tenantId, DeviceProfile deviceProfile, OtaPackageType otaPackageType) { Consumer updateConsumer; - switch (firmwareType) { - case FIRMWARE: - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl); - break; - case SOFTWARE: - getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl); - break; - default: - log.warn("Unsupported firmware type: [{}]", firmwareType); - return; - } - if (deviceProfile.getFirmwareId() != null) { long ts = System.currentTimeMillis(); - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType); + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, otaPackageType); } else { - updateConsumer = d -> remove(d, firmwareType); + updateConsumer = d -> remove(d, otaPackageType); } PageLink pageLink = new PageLink(100); PageData pageData; do { - pageData = getDevicesFunction.apply(pageLink); + pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId, deviceProfile.getId(), otaPackageType, pageLink); pageData.getData().forEach(updateConsumer); if (pageData.hasNext()) { @@ -206,60 +192,60 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } @Override - public boolean process(ToFirmwareStateServiceMsg msg) { + public boolean process(ToOtaPackageStateServiceMsg msg) { boolean isSuccess = false; - FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); + OtaPackageId targetOtaPackageId = new OtaPackageId(new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB())); DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); - FirmwareType firmwareType = FirmwareType.valueOf(msg.getType()); + OtaPackageType firmwareType = OtaPackageType.valueOf(msg.getType()); long ts = msg.getTs(); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); } else { - FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); - if (currentFirmwareId == null) { + OtaPackageId currentOtaPackageId = OtaPackageUtil.getOtaPackageId(device, firmwareType); + if (currentOtaPackageId == null) { DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()); - currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); + currentOtaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, firmwareType); } - if (targetFirmwareId.equals(currentFirmwareId)) { - update(device, firmwareService.findFirmwareInfoById(device.getTenantId(), targetFirmwareId), ts); + if (targetOtaPackageId.equals(currentOtaPackageId)) { + update(device, otaPackageService.findOtaPackageInfoById(device.getTenantId(), targetOtaPackageId), ts); isSuccess = true; } else { - log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetFirmwareId, currentFirmwareId); + log.warn("[{}] [{}] Can`t update firmware for the device, target firmwareId: [{}], current firmwareId: [{}]!", tenantId, deviceId, targetOtaPackageId, currentOtaPackageId); } } return isSuccess; } - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) { - ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() + private void send(TenantId tenantId, DeviceId deviceId, OtaPackageId firmwareId, long ts, OtaPackageType firmwareType) { + ToOtaPackageStateServiceMsg msg = ToOtaPackageStateServiceMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) .setDeviceIdMSB(deviceId.getId().getMostSignificantBits()) .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) - .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) - .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) + .setOtaPackageIdMSB(firmwareId.getId().getMostSignificantBits()) + .setOtaPackageIdLSB(firmwareId.getId().getLeastSignificantBits()) .setType(firmwareType.name()) .setTs(ts) .build(); - FirmwareInfo firmware = firmwareService.findFirmwareInfoById(tenantId, firmwareId); + OtaPackageInfo firmware = otaPackageService.findOtaPackageInfoById(tenantId, firmwareId); if (firmware == null) { log.warn("[{}] Failed to send firmware update because firmware was already deleted", firmwareId); return; } - TopicPartitionInfo tpi = new TopicPartitionInfo(fwStateMsgProducer.getDefaultTopic(), null, null, false); - fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); + TopicPartitionInfo tpi = new TopicPartitionInfo(otaPackageStateMsgProducer.getDefaultTopic(), null, null, false); + otaPackageStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); List telemetry = new ArrayList<>(); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name()))); + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.QUEUED.name()))); telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { @Override @@ -275,11 +261,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } - private void update(Device device, FirmwareInfo firmware, long ts) { + private void update(Device device, OtaPackageInfo firmware, long ts) { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name())); + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.INITIATED.name())); telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { @Override @@ -313,14 +299,14 @@ public class DefaultFirmwareStateService implements FirmwareStateService { }); } - private void remove(Device device, FirmwareType firmwareType) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType), + private void remove(Device device, OtaPackageType firmwareType) { + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType), new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success remove target firmware attributes!", device.getId()); Set keysToNotify = new HashSet<>(); - FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); + OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); } diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java similarity index 79% rename from application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java rename to application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java index 8562f096c9..9392d1c888 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/OtaPackageStateService.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.service.firmware; +package org.thingsboard.server.service.ota; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; -public interface FirmwareStateService { +public interface OtaPackageStateService { void update(Device device, Device oldDevice); void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged); - boolean process(ToFirmwareStateServiceMsg msg); + boolean process(ToOtaPackageStateServiceMsg msg); } diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java index b8c8698d52..ceb364f921 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbCoreConsumerService.java @@ -50,7 +50,7 @@ import org.thingsboard.server.gen.transport.TransportProtos.TbSubscriptionCloseP import org.thingsboard.server.gen.transport.TransportProtos.TbTimeSeriesUpdateProto; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.TransportToDeviceActorMsg; import org.thingsboard.server.queue.TbQueueConsumer; @@ -60,7 +60,7 @@ import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.edge.EdgeNotificationService; -import org.thingsboard.server.service.firmware.FirmwareStateService; +import org.thingsboard.server.service.ota.OtaPackageStateService; import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.processing.IdMsgPair; @@ -75,7 +75,6 @@ import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWra import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -101,9 +100,9 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> mainConsumer; @@ -113,10 +112,10 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService> usageStatsConsumer; - private final TbQueueConsumer> firmwareStatesConsumer; + private final TbQueueConsumer> firmwareStatesConsumer; protected volatile ExecutorService usageStatsExecutor; @@ -135,11 +134,11 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService { while (!stopped) { try { - List> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); + List> msgs = firmwareStatesConsumer.poll(getNotificationPollDuration()); if (msgs.isEmpty()) { continue; } long timeToSleep = maxProcessingTimeoutPerRecord; - for (TbProtoQueueMsg msg : msgs) { + for (TbProtoQueueMsg msg : msgs) { try { long startTime = System.currentTimeMillis(); - boolean isSuccessUpdate = handleFirmwareUpdates(msg); + boolean isSuccessUpdate = handleOtaPackageUpdates(msg); long endTime = System.currentTimeMillis(); long spentTime = endTime - startTime; timeToSleep = timeToSleep - spentTime; @@ -402,7 +401,7 @@ public class DefaultTbCoreConsumerService extends AbstractConsumerService msg) { + private boolean handleOtaPackageUpdates(TbProtoQueueMsg msg) { return firmwareStateService.process(msg.getValue()); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java index 914b9ce7fa..540a47e8b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java +++ b/application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java @@ -30,7 +30,7 @@ import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; @@ -46,7 +46,7 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; import org.thingsboard.server.common.data.id.TbResourceId; @@ -63,7 +63,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -135,7 +135,7 @@ public class AccessValidator { protected ResourceService resourceService; @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; private ExecutorService executor; @@ -232,8 +232,8 @@ public class AccessValidator { case TB_RESOURCE: validateResource(currentUser, operation, entityId, callback); return; - case FIRMWARE: - validateFirmware(currentUser, operation, entityId, callback); + case OTA_PACKAGE: + validateOtaPackage(currentUser, operation, entityId, callback); return; default: //TODO: add support of other entities @@ -300,20 +300,20 @@ public class AccessValidator { } } - private void validateFirmware(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { + private void validateOtaPackage(final SecurityUser currentUser, Operation operation, EntityId entityId, FutureCallback callback) { if (currentUser.isSystemAdmin()) { callback.onSuccess(ValidationResult.accessDenied(SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION)); } else { - FirmwareInfo firmware = firmwareService.findFirmwareInfoById(currentUser.getTenantId(), new FirmwareId(entityId.getId())); - if (firmware == null) { - callback.onSuccess(ValidationResult.entityNotFound("Firmware with requested id wasn't found!")); + OtaPackageInfo otaPackage = otaPackageService.findOtaPackageInfoById(currentUser.getTenantId(), new OtaPackageId(entityId.getId())); + if (otaPackage == null) { + callback.onSuccess(ValidationResult.entityNotFound("OtaPackage with requested id wasn't found!")); } else { try { - accessControlService.checkPermission(currentUser, Resource.FIRMWARE, operation, entityId, firmware); + accessControlService.checkPermission(currentUser, Resource.OTA_PACKAGE, operation, entityId, otaPackage); } catch (ThingsboardException e) { callback.onSuccess(ValidationResult.accessDenied(e.getMessage())); } - callback.onSuccess(ValidationResult.ok(firmware)); + callback.onSuccess(ValidationResult.ok(otaPackage)); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 75119c402d..43c420a94a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -38,7 +38,7 @@ public enum Resource { DEVICE_PROFILE(EntityType.DEVICE_PROFILE), API_USAGE_STATE(EntityType.API_USAGE_STATE), TB_RESOURCE(EntityType.TB_RESOURCE), - FIRMWARE(EntityType.FIRMWARE), + OTA_PACKAGE(EntityType.OTA_PACKAGE), EDGE(EntityType.EDGE); private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index af08e38087..8b4d44e938 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -42,7 +42,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.DEVICE_PROFILE, tenantEntityPermissionChecker); put(Resource.API_USAGE_STATE, tenantEntityPermissionChecker); put(Resource.TB_RESOURCE, tbResourcePermissionChecker); - put(Resource.FIRMWARE, tenantEntityPermissionChecker); + put(Resource.OTA_PACKAGE, tenantEntityPermissionChecker); put(Resource.EDGE, tenantEntityPermissionChecker); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index bd2df29617..76bbe1f518 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -26,27 +26,27 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.ApiUsageState; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -64,7 +64,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.device.provision.ProvisionFailedException; import org.thingsboard.server.dao.device.provision.ProvisionRequest; import org.thingsboard.server.dao.device.provision.ProvisionResponse; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.gen.transport.TransportProtos; @@ -124,8 +124,8 @@ public class DefaultTransportApiService implements TransportApiService { private final DataDecodingEncodingService dataDecodingEncodingService; private final DeviceProvisionService deviceProvisionService; private final TbResourceService resourceService; - private final FirmwareService firmwareService; - private final FirmwareDataCache firmwareDataCache; + private final OtaPackageService otaPackageService; + private final OtaPackageDataCache otaPackageDataCache; private final ConcurrentMap deviceCreationLocks = new ConcurrentHashMap<>(); @@ -134,7 +134,7 @@ public class DefaultTransportApiService implements TransportApiService { RelationService relationService, DeviceCredentialsService deviceCredentialsService, DeviceStateService deviceStateService, DbCallbackExecutorService dbCallbackExecutorService, TbClusterService tbClusterService, DataDecodingEncodingService dataDecodingEncodingService, - DeviceProvisionService deviceProvisionService, TbResourceService resourceService, FirmwareService firmwareService, FirmwareDataCache firmwareDataCache) { + DeviceProvisionService deviceProvisionService, TbResourceService resourceService, OtaPackageService otaPackageService, OtaPackageDataCache otaPackageDataCache) { this.deviceProfileCache = deviceProfileCache; this.tenantProfileCache = tenantProfileCache; this.apiUsageStateService = apiUsageStateService; @@ -147,8 +147,8 @@ public class DefaultTransportApiService implements TransportApiService { this.dataDecodingEncodingService = dataDecodingEncodingService; this.deviceProvisionService = deviceProvisionService; this.resourceService = resourceService; - this.firmwareService = firmwareService; - this.firmwareDataCache = firmwareDataCache; + this.otaPackageService = otaPackageService; + this.otaPackageDataCache = otaPackageDataCache; } @Override @@ -184,8 +184,8 @@ public class DefaultTransportApiService implements TransportApiService { result = handle(transportApiRequestMsg.getDeviceRequestMsg()); } else if (transportApiRequestMsg.hasDeviceCredentialsRequestMsg()) { result = handle(transportApiRequestMsg.getDeviceCredentialsRequestMsg()); - } else if (transportApiRequestMsg.hasFirmwareRequestMsg()) { - result = handle(transportApiRequestMsg.getFirmwareRequestMsg()); + } else if (transportApiRequestMsg.hasOtaPackageRequestMsg()) { + result = handle(transportApiRequestMsg.getOtaPackageRequestMsg()); } return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), @@ -511,50 +511,50 @@ public class DefaultTransportApiService implements TransportApiService { } } - private ListenableFuture handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { + private ListenableFuture handle(TransportProtos.GetOtaPackageRequestMsg requestMsg) { TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); - FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType()); + OtaPackageType otaPackageType = OtaPackageType.valueOf(requestMsg.getType()); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { return getEmptyTransportApiResponseFuture(); } - FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); - if (firmwareId == null) { + OtaPackageId otaPackageId = OtaPackageUtil.getOtaPackageId(device, otaPackageType); + if (otaPackageId == null) { DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId()); - firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); + otaPackageId = OtaPackageUtil.getOtaPackageId(deviceProfile, otaPackageType); } - TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); + TransportProtos.GetOtaPackageResponseMsg.Builder builder = TransportProtos.GetOtaPackageResponseMsg.newBuilder(); - if (firmwareId == null) { + if (otaPackageId == null) { builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); } else { - FirmwareInfo firmwareInfo = firmwareService.findFirmwareInfoById(tenantId, firmwareId); + OtaPackageInfo otaPackageInfo = otaPackageService.findOtaPackageInfoById(tenantId, otaPackageId); - if (firmwareInfo == null) { + if (otaPackageInfo == null) { builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); } else { builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); - builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); - builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); - builder.setType(firmwareInfo.getType().name()); - builder.setTitle(firmwareInfo.getTitle()); - builder.setVersion(firmwareInfo.getVersion()); - builder.setFileName(firmwareInfo.getFileName()); - builder.setContentType(firmwareInfo.getContentType()); - if (!firmwareDataCache.has(firmwareId.toString())) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, firmwareId); - firmwareDataCache.put(firmwareId.toString(), firmware.getData().array()); + builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits()); + builder.setOtaPackageIdLSB(otaPackageId.getId().getLeastSignificantBits()); + builder.setType(otaPackageInfo.getType().name()); + builder.setTitle(otaPackageInfo.getTitle()); + builder.setVersion(otaPackageInfo.getVersion()); + builder.setFileName(otaPackageInfo.getFileName()); + builder.setContentType(otaPackageInfo.getContentType()); + if (!otaPackageDataCache.has(otaPackageId.toString())) { + OtaPackage otaPackage = otaPackageService.findOtaPackageById(tenantId, otaPackageId); + otaPackageDataCache.put(otaPackageId.toString(), otaPackage.getData().array()); } } } return Futures.immediateFuture( TransportApiResponseMsg.newBuilder() - .setFirmwareResponseMsg(builder.build()) + .setOtaPackageResponseMsg(builder.build()) .build()); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 7fde122cf6..ae628bf9f6 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -371,7 +371,7 @@ caffeine: tokensOutdatageTime: timeToLiveInMinutes: 20000 maxSize: 10000 - firmwares: + otaPackages: timeToLiveInMinutes: 60 maxSize: 10 edges: @@ -497,7 +497,7 @@ audit-log: "device_profile": "${AUDIT_LOG_MASK_DEVICE_PROFILE:W}" "edge": "${AUDIT_LOG_MASK_EDGE:W}" "tb_resource": "${AUDIT_LOG_MASK_RESOURCE:W}" - "firmware": "${AUDIT_LOG_MASK_FIRMWARE:W}" + "ota_package": "${AUDIT_LOG_MASK_OTA_PACKAGE:W}" sink: # Type of external sink. possible options: none, elasticsearch type: "${AUDIT_LOG_SINK_TYPE:none}" @@ -749,7 +749,7 @@ queue: sasl.config: "${TB_QUEUE_KAFKA_CONFLUENT_SASL_JAAS_CONFIG:org.apache.kafka.common.security.plain.PlainLoginModule required username=\"CLUSTER_API_KEY\" password=\"CLUSTER_API_SECRET\";}" security.protocol: "${TB_QUEUE_KAFKA_CONFLUENT_SECURITY_PROTOCOL:SASL_SSL}" consumer-properties-per-topic: - tb_firmware: + tb_ota_package: - key: max.poll.records value: 10 other: @@ -759,7 +759,7 @@ queue: transport-api: "${TB_QUEUE_KAFKA_TA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" notifications: "${TB_QUEUE_KAFKA_NOTIFICATIONS_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:1;min.insync.replicas:1}" js-executor: "${TB_QUEUE_KAFKA_JE_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:104857600;partitions:100;min.insync.replicas:1}" - fw-updates: "${TB_QUEUE_KAFKA_FW_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" + ota-updates: "${TB_QUEUE_KAFKA_OTA_TOPIC_PROPERTIES:retention.ms:604800000;segment.bytes:26214400;retention.bytes:1048576000;partitions:10;min.insync.replicas:1}" consumer-stats: enabled: "${TB_QUEUE_KAFKA_CONSUMER_STATS_ENABLED:true}" print-interval-ms: "${TB_QUEUE_KAFKA_CONSUMER_STATS_MIN_PRINT_INTERVAL_MS:60000}" @@ -830,10 +830,10 @@ queue: poll-interval: "${TB_QUEUE_CORE_POLL_INTERVAL_MS:25}" partitions: "${TB_QUEUE_CORE_PARTITIONS:10}" pack-processing-timeout: "${TB_QUEUE_CORE_PACK_PROCESSING_TIMEOUT_MS:2000}" - firmware: - topic: "${TB_QUEUE_CORE_FW_TOPIC:tb_firmware}" - pack-interval-ms: "${TB_QUEUE_CORE_FW_PACK_INTERVAL_MS:60000}" - pack-size: "${TB_QUEUE_CORE_FW_PACK_SIZE:100}" + ota: + topic: "${TB_QUEUE_CORE_OTA_TOPIC:tb_ota_package}" + pack-interval-ms: "${TB_QUEUE_CORE_OTA_PACK_INTERVAL_MS:60000}" + pack-size: "${TB_QUEUE_CORE_OTA_PACK_SIZE:100}" usage-stats-topic: "${TB_QUEUE_US_TOPIC:tb_usage_stats}" stats: enabled: "${TB_QUEUE_CORE_STATS_ENABLED:true}" diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java similarity index 66% rename from application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java rename to application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java index ec36be13a5..9b73053831 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -25,11 +25,10 @@ import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; -import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -41,11 +40,11 @@ import java.util.Collections; import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { +public abstract class BaseOtaPackageControllerTest extends AbstractControllerTest { - private IdComparator idComparator = new IdComparator<>(); + private IdComparator idComparator = new IdComparator<>(); public static final String TITLE = "My firmware"; private static final String FILE_NAME = "filename.txt"; @@ -93,13 +92,13 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testSaveFirmware() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -112,19 +111,19 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest save(savedFirmwareInfo); - FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); } @Test public void testSaveFirmwareData() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -137,12 +136,12 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest save(savedFirmwareInfo); - FirmwareInfo foundFirmwareInfo = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmwareInfo = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertEquals(foundFirmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); Assert.assertEquals(FILE_NAME, savedFirmware.getFileName()); Assert.assertEquals(CONTENT_TYPE, savedFirmware.getContentType()); @@ -150,97 +149,97 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testUpdateFirmwareFromDifferentTenant() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); loginDifferentTenant(); - doPost("/api/firmware", savedFirmwareInfo, FirmwareInfo.class, status().isForbidden()); + doPost("/api/otaPackage", savedFirmwareInfo, OtaPackageInfo.class, status().isForbidden()); deleteDifferentTenant(); } @Test public void testFindFirmwareInfoById() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); - FirmwareInfo foundFirmware = doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString(), FirmwareInfo.class); + OtaPackageInfo foundFirmware = doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString(), OtaPackageInfo.class); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmwareInfo, foundFirmware); } @Test public void testFindFirmwareById() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - Firmware foundFirmware = doGet("/api/firmware/" + savedFirmwareInfo.getId().getId().toString(), Firmware.class); + OtaPackage foundFirmware = doGet("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString(), OtaPackage.class); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); } @Test public void testDeleteFirmware() throws Exception { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); - doDelete("/api/firmware/" + savedFirmwareInfo.getId().getId().toString()) + doDelete("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString()) .andExpect(status().isOk()); - doGet("/api/firmware/info/" + savedFirmwareInfo.getId().getId().toString()) + doGet("/api/otaPackage/info/" + savedFirmwareInfo.getId().getId().toString()) .andExpect(status().isNotFound()); } @Test public void testFindTenantFirmwares() throws Exception { - List firmwares = new ArrayList<>(); + List otaPackages = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); if (i > 100) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - firmwares.add(new FirmwareInfo(savedFirmware)); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + otaPackages.add(new OtaPackageInfo(savedFirmware)); } else { - firmwares.add(savedFirmwareInfo); + otaPackages.add(savedFirmwareInfo); } } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(24); - PageData pageData; + PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/firmwares?", + pageData = doGetTypedWithPageLink("/api/otaPackages?", new TypeReference<>() { }, pageLink); loadedFirmwares.addAll(pageData.getData()); @@ -249,41 +248,41 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - Collections.sort(firmwares, idComparator); + Collections.sort(otaPackages, idComparator); Collections.sort(loadedFirmwares, idComparator); - Assert.assertEquals(firmwares, loadedFirmwares); + Assert.assertEquals(otaPackages, loadedFirmwares); } @Test public void testFindTenantFirmwaresByHasData() throws Exception { - List firmwaresWithData = new ArrayList<>(); - List firmwaresWithoutData = new ArrayList<>(); + List otaPackagesWithData = new ArrayList<>(); + List otaPackagesWithoutData = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); - FirmwareInfo savedFirmwareInfo = save(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = save(firmwareInfo); if (i > 100) { MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); - Firmware savedFirmware = savaData("/api/firmware/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - firmwaresWithData.add(new FirmwareInfo(savedFirmware)); + OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); + otaPackagesWithData.add(new OtaPackageInfo(savedFirmware)); } else { - firmwaresWithoutData.add(savedFirmwareInfo); + otaPackagesWithoutData.add(savedFirmwareInfo); } } - List loadedFirmwaresWithData = new ArrayList<>(); + List loadedFirmwaresWithData = new ArrayList<>(); PageLink pageLink = new PageLink(24); - PageData pageData; + PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?", + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/true?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithData.addAll(pageData.getData()); @@ -292,10 +291,10 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - List loadedFirmwaresWithoutData = new ArrayList<>(); + List loadedFirmwaresWithoutData = new ArrayList<>(); pageLink = new PageLink(24); do { - pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?", + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/false?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithoutData.addAll(pageData.getData()); @@ -304,25 +303,25 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest } } while (pageData.hasNext()); - Collections.sort(firmwaresWithData, idComparator); - Collections.sort(firmwaresWithoutData, idComparator); + Collections.sort(otaPackagesWithData, idComparator); + Collections.sort(otaPackagesWithoutData, idComparator); Collections.sort(loadedFirmwaresWithData, idComparator); Collections.sort(loadedFirmwaresWithoutData, idComparator); - Assert.assertEquals(firmwaresWithData, loadedFirmwaresWithData); - Assert.assertEquals(firmwaresWithoutData, loadedFirmwaresWithoutData); + Assert.assertEquals(otaPackagesWithData, loadedFirmwaresWithData); + Assert.assertEquals(otaPackagesWithoutData, loadedFirmwaresWithoutData); } - private FirmwareInfo save(FirmwareInfo firmwareInfo) throws Exception { - return doPost("/api/firmware", firmwareInfo, FirmwareInfo.class); + private OtaPackageInfo save(OtaPackageInfo firmwareInfo) throws Exception { + return doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); } - protected Firmware savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { + protected OtaPackage savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); postRequest.file(content); setJwtToken(postRequest); - return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), Firmware.class); + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackage.class); } } diff --git a/application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java b/application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java similarity index 82% rename from application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java rename to application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java index a0e4a838ca..92bd9ba9c6 100644 --- a/application/src/test/java/org/thingsboard/server/controller/sql/FirmwareControllerSqlTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/sql/OtaPackageControllerSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.controller.sql; -import org.thingsboard.server.controller.BaseFirmwareControllerTest; +import org.thingsboard.server.controller.BaseOtaPackageControllerTest; import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest -public class FirmwareControllerSqlTest extends BaseFirmwareControllerTest { +public class OtaPackageControllerSqlTest extends BaseOtaPackageControllerTest { } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java similarity index 84% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java index 1acb09b28e..a864fc6dba 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/CaffeineFirmwareCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @RequiredArgsConstructor -public class CaffeineFirmwareCache implements FirmwareDataCache { +public class CaffeineOtaPackageCache implements OtaPackageDataCache { private final CacheManager cacheManager; @@ -36,7 +36,7 @@ public class CaffeineFirmwareCache implements FirmwareDataCache { @Override public byte[] get(String key, int chunkSize, int chunk) { - byte[] data = cacheManager.getCache(FIRMWARE_CACHE).get(key, byte[].class); + byte[] data = cacheManager.getCache(OTA_PACKAGE_CACHE).get(key, byte[].class); if (chunkSize < 1) { return data; @@ -58,11 +58,11 @@ public class CaffeineFirmwareCache implements FirmwareDataCache { @Override public void put(String key, byte[] value) { - cacheManager.getCache(FIRMWARE_CACHE).putIfAbsent(key, value); + cacheManager.getCache(OTA_PACKAGE_CACHE).putIfAbsent(key, value); } @Override public void evict(String key) { - cacheManager.getCache(FIRMWARE_CACHE).evict(key); + cacheManager.getCache(OTA_PACKAGE_CACHE).evict(key); } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java similarity index 82% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java index 99f2f0d521..77057406c2 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/FirmwareDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/OtaPackageDataCache.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; -public interface FirmwareDataCache { +public interface OtaPackageDataCache { byte[] get(String key); @@ -25,8 +25,8 @@ public interface FirmwareDataCache { void evict(String key); - default boolean has(String firmwareId) { - byte[] data = get(firmwareId, 1, 0); + default boolean has(String otaPackageId) { + byte[] data = get(otaPackageId, 1, 0); return data != null && data.length > 0; } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java similarity index 78% rename from common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java rename to common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java index 08dd6facc2..1e4ae53829 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/firmware/RedisFirmwareDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.cache.firmware; +package org.thingsboard.server.cache.ota; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -21,12 +21,12 @@ import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.stereotype.Service; -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @RequiredArgsConstructor -public class RedisFirmwareDataCache implements FirmwareDataCache { +public class RedisOtaPackageDataCache implements OtaPackageDataCache { private final RedisConnectionFactory redisConnectionFactory; @@ -39,30 +39,30 @@ public class RedisFirmwareDataCache implements FirmwareDataCache { public byte[] get(String key, int chunkSize, int chunk) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { if (chunkSize == 0) { - return connection.get(toFirmwareCacheKey(key)); + return connection.get(toOtaPackageCacheKey(key)); } int startIndex = chunkSize * chunk; int endIndex = startIndex + chunkSize - 1; - return connection.getRange(toFirmwareCacheKey(key), startIndex, endIndex); + return connection.getRange(toOtaPackageCacheKey(key), startIndex, endIndex); } } @Override public void put(String key, byte[] value) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { - connection.set(toFirmwareCacheKey(key), value); + connection.set(toOtaPackageCacheKey(key), value); } } @Override public void evict(String key) { try (RedisConnection connection = redisConnectionFactory.getConnection()) { - connection.del(toFirmwareCacheKey(key)); + connection.del(toOtaPackageCacheKey(key)); } } - private byte[] toFirmwareCacheKey(String key) { - return String.format("%s::%s", FIRMWARE_CACHE, key).getBytes(); + private byte[] toOtaPackageCacheKey(String key) { + return String.format("%s::%s", OTA_PACKAGE_CACHE, key).getBytes(); } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index b4692ec5bf..761211f32c 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.DeviceCredentials; @@ -63,9 +64,9 @@ public interface DeviceService { PageData findDevicesByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink); + Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType); PageData findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java deleted file mode 100644 index eeaafbd777..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.firmware; - -import com.google.common.util.concurrent.ListenableFuture; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; - -import java.nio.ByteBuffer; - -public interface FirmwareService { - - FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo); - - Firmware saveFirmware(Firmware firmware); - - String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data); - - Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId); - - FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId); - - ListenableFuture findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId); - - PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); - - PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); - - void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); - - void deleteFirmwaresByTenantId(TenantId tenantId); -} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java new file mode 100644 index 0000000000..589bdf14b6 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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.dao.ota; + +import com.google.common.util.concurrent.ListenableFuture; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; + +import java.nio.ByteBuffer; + +public interface OtaPackageService { + + OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo); + + OtaPackage saveOtaPackage(OtaPackage otaPackage); + + String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data); + + OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId); + + OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId); + + ListenableFuture findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId); + + PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink); + + PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); + + void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId); + + void deleteOtaPackagesByTenantId(TenantId tenantId); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index ce4576705b..3cc3f56737 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -29,5 +29,5 @@ public class CacheConstants { public static final String DEVICE_PROFILE_CACHE = "deviceProfiles"; public static final String ATTRIBUTES_CACHE = "attributes"; public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime"; - public static final String FIRMWARE_CACHE = "firmwares"; + public static final String OTA_PACKAGE_CACHE = "otaPackages"; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index bce3ba703f..9abc619b48 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.NoXss; @@ -32,7 +32,7 @@ import java.io.IOException; @EqualsAndHashCode(callSuper = true) @Slf4j -public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasFirmware { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasOtaPackage { private static final long serialVersionUID = 2807343040519543363L; @@ -49,8 +49,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen @JsonIgnore private byte[] deviceDataBytes; - private FirmwareId firmwareId; - private FirmwareId softwareId; + private OtaPackageId firmwareId; + private OtaPackageId softwareId; public Device() { super(); @@ -167,19 +167,19 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen return getName(); } - public FirmwareId getFirmwareId() { + public OtaPackageId getFirmwareId() { return firmwareId; } - public void setFirmwareId(FirmwareId firmwareId) { + public void setFirmwareId(OtaPackageId firmwareId) { this.firmwareId = firmwareId; } - public FirmwareId getSoftwareId() { + public OtaPackageId getSoftwareId() { return softwareId; } - public void setSoftwareId(FirmwareId softwareId) { + public void setSoftwareId(OtaPackageId softwareId) { this.softwareId = softwareId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index e35fbb84a0..13c4692976 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -23,7 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.validation.NoXss; @@ -37,7 +37,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @Data @EqualsAndHashCode(callSuper = true) @Slf4j -public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasFirmware { +public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasOtaPackage { private TenantId tenantId; @NoXss @@ -60,9 +60,9 @@ public class DeviceProfile extends SearchTextBased implements H @NoXss private String provisionDeviceKey; - private FirmwareId firmwareId; + private OtaPackageId firmwareId; - private FirmwareId softwareId; + private OtaPackageId softwareId; public DeviceProfile() { super(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index d3508d42dd..cf6c6fd9a7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, FIRMWARE, EDGE; + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE, API_USAGE_STATE, TB_RESOURCE, OTA_PACKAGE, EDGE; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java similarity index 80% rename from common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java rename to common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java index ae05829092..09320e13d0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasOtaPackage.java @@ -15,11 +15,11 @@ */ package org.thingsboard.server.common.data; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; -public interface HasFirmware { +public interface HasOtaPackage { - FirmwareId getFirmwareId(); + OtaPackageId getFirmwareId(); - FirmwareId getSoftwareId(); + OtaPackageId getSoftwareId(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java similarity index 82% rename from common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java rename to common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java index b4155a578a..6110310cd3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Firmware.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java @@ -17,27 +17,27 @@ package org.thingsboard.server.common.data; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import java.nio.ByteBuffer; @Data @EqualsAndHashCode(callSuper = true) -public class Firmware extends FirmwareInfo { +public class OtaPackage extends OtaPackageInfo { private static final long serialVersionUID = 3091601761339422546L; private transient ByteBuffer data; - public Firmware() { + public OtaPackage() { super(); } - public Firmware(FirmwareId id) { + public OtaPackage(OtaPackageId id) { super(id); } - public Firmware(Firmware firmware) { + public OtaPackage(OtaPackage firmware) { super(firmware); this.data = firmware.getData(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java similarity index 58% rename from common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java index 33b529e303..5a33a95215 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java @@ -19,22 +19,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; @Slf4j @Data @EqualsAndHashCode(callSuper = true) -public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { +public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId { private static final long serialVersionUID = 3168391583570815419L; private TenantId tenantId; private DeviceProfileId deviceProfileId; - private FirmwareType type; + private OtaPackageType type; private String title; private String version; private boolean hasData; @@ -45,27 +45,27 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo private Long dataSize; - public FirmwareInfo() { + public OtaPackageInfo() { super(); } - public FirmwareInfo(FirmwareId id) { + public OtaPackageInfo(OtaPackageId id) { super(id); } - public FirmwareInfo(FirmwareInfo firmwareInfo) { - super(firmwareInfo); - this.tenantId = firmwareInfo.getTenantId(); - this.deviceProfileId = firmwareInfo.getDeviceProfileId(); - this.type = firmwareInfo.getType(); - this.title = firmwareInfo.getTitle(); - this.version = firmwareInfo.getVersion(); - this.hasData = firmwareInfo.isHasData(); - this.fileName = firmwareInfo.getFileName(); - this.contentType = firmwareInfo.getContentType(); - this.checksumAlgorithm = firmwareInfo.getChecksumAlgorithm(); - this.checksum = firmwareInfo.getChecksum(); - this.dataSize = firmwareInfo.getDataSize(); + public OtaPackageInfo(OtaPackageInfo otaPackageInfo) { + super(otaPackageInfo); + this.tenantId = otaPackageInfo.getTenantId(); + this.deviceProfileId = otaPackageInfo.getDeviceProfileId(); + this.type = otaPackageInfo.getType(); + this.title = otaPackageInfo.getTitle(); + this.version = otaPackageInfo.getVersion(); + this.hasData = otaPackageInfo.isHasData(); + this.fileName = otaPackageInfo.getFileName(); + this.contentType = otaPackageInfo.getContentType(); + this.checksumAlgorithm = otaPackageInfo.getChecksumAlgorithm(); + this.checksum = otaPackageInfo.getChecksum(); + this.dataSize = otaPackageInfo.getDataSize(); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index df2846eead..a4b2327c75 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -71,8 +71,8 @@ public class EntityIdFactory { return new ApiUsageStateId(uuid); case TB_RESOURCE: return new TbResourceId(uuid); - case FIRMWARE: - return new FirmwareId(uuid); + case OTA_PACKAGE: + return new OtaPackageId(uuid); case EDGE: return new EdgeId(uuid); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java similarity index 79% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java index 3cdee53f58..0442792238 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/FirmwareId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OtaPackageId.java @@ -22,23 +22,23 @@ import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class FirmwareId extends UUIDBased implements EntityId { +public class OtaPackageId extends UUIDBased implements EntityId { private static final long serialVersionUID = 1L; @JsonCreator - public FirmwareId(@JsonProperty("id") UUID id) { + public OtaPackageId(@JsonProperty("id") UUID id) { super(id); } - public static FirmwareId fromString(String firmwareId) { - return new FirmwareId(UUID.fromString(firmwareId)); + public static OtaPackageId fromString(String firmwareId) { + return new OtaPackageId(UUID.fromString(firmwareId)); } @JsonIgnore @Override public EntityType getEntityType() { - return EntityType.FIRMWARE; + return EntityType.OTA_PACKAGE; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java similarity index 93% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java index 3998482e35..ce33463054 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/ChecksumAlgorithm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/ChecksumAlgorithm.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; public enum ChecksumAlgorithm { MD5, diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java index cb38b6724c..0528b9dfe3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageKey.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.Getter; -public enum FirmwareKey { +public enum OtaPackageKey { TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"); @Getter private final String value; - FirmwareKey(String value) { + OtaPackageKey(String value) { this.value = value; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java similarity index 86% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java index 5f6aa0d925..cab8cf1847 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageType.java @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.Getter; -public enum FirmwareType { +public enum OtaPackageType { FIRMWARE("fw"), SOFTWARE("sw"); @Getter private final String keyPrefix; - FirmwareType(String keyPrefix) { + OtaPackageType(String keyPrefix) { this.keyPrefix = keyPrefix; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java similarity index 88% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java index 3e46174792..caa043c595 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUpdateStatus.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUpdateStatus.java @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; -public enum FirmwareUpdateStatus { +public enum OtaPackageUpdateStatus { QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java similarity index 52% rename from common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java rename to common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java index 646ad24173..f2b29ec1f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/ota/OtaPackageUtil.java @@ -13,21 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.firmware; +package org.thingsboard.server.common.data.ota; import lombok.extern.slf4j.Slf4j; -import org.thingsboard.server.common.data.HasFirmware; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.HasOtaPackage; +import org.thingsboard.server.common.data.id.OtaPackageId; import java.util.ArrayList; import java.util.Collections; import java.util.List; - -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; +import java.util.function.Supplier; @Slf4j -public class FirmwareUtil { +public class OtaPackageUtil { public static final List ALL_FW_ATTRIBUTE_KEYS; @@ -35,19 +33,19 @@ public class FirmwareUtil { static { ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>(); - for (FirmwareKey key : FirmwareKey.values()) { - ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key)); + for (OtaPackageKey key : OtaPackageKey.values()) { + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.FIRMWARE, key)); } ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>(); - for (FirmwareKey key : FirmwareKey.values()) { - ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key)); + for (OtaPackageKey key : OtaPackageKey.values()) { + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(OtaPackageType.SOFTWARE, key)); } } - public static List getAttributeKeys(FirmwareType firmwareType) { + public static List getAttributeKeys(OtaPackageType firmwareType) { switch (firmwareType) { case FIRMWARE: return ALL_FW_ATTRIBUTE_KEYS; @@ -57,35 +55,46 @@ public class FirmwareUtil { return Collections.emptyList(); } - public static String getAttributeKey(FirmwareType type, FirmwareKey key) { + public static String getAttributeKey(OtaPackageType type, OtaPackageKey key) { return type.getKeyPrefix() + "_" + key.getValue(); } - public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getTargetTelemetryKey(OtaPackageType type, OtaPackageKey key) { return getTelemetryKey("target_", type, key); } - public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getCurrentTelemetryKey(OtaPackageType type, OtaPackageKey key) { return getTelemetryKey("current_", type, key); } - private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) { + private static String getTelemetryKey(String prefix, OtaPackageType type, OtaPackageKey key) { return prefix + type.getKeyPrefix() + "_" + key.getValue(); } - public static String getTelemetryKey(FirmwareType type, FirmwareKey key) { + public static String getTelemetryKey(OtaPackageType type, OtaPackageKey key) { return type.getKeyPrefix() + "_" + key.getValue(); } - public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) { - switch (firmwareType) { + public static OtaPackageId getOtaPackageId(HasOtaPackage entity, OtaPackageType type) { + switch (type) { case FIRMWARE: return entity.getFirmwareId(); case SOFTWARE: return entity.getSoftwareId(); default: - log.warn("Unsupported firmware type: [{}]", firmwareType); + log.warn("Unsupported ota package type: [{}]", type); return null; } } + + public static T getByOtaPackageType(Supplier firmwareSupplier, Supplier softwareSupplier, OtaPackageType type) { + switch (type) { + case FIRMWARE: + return firmwareSupplier.get(); + case SOFTWARE: + return softwareSupplier.get(); + default: + throw new RuntimeException("Unsupported OtaPackage type: " + type); + } + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java index 1a6a1a56e4..624918c8dd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/kafka/TbKafkaTopicConfigs.java @@ -38,7 +38,7 @@ public class TbKafkaTopicConfigs { private String notificationsProperties; @Value("${queue.kafka.topic-properties.js-executor}") private String jsExecutorProperties; - @Value("${queue.kafka.topic-properties.fw-updates:}") + @Value("${queue.kafka.topic-properties.ota-updates:}") private String fwUpdatesProperties; @Getter diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java index c2f4f4f750..d89b03c744 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsMonolithQueueFactory.java @@ -187,14 +187,14 @@ public class AwsSqsMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java index 0267cdecb8..eaa73bc108 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/AwsSqsTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -177,14 +177,14 @@ public class AwsSqsTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbAwsSqsConsumerTemplate<>(transportApiAdmin, sqsSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbAwsSqsProducerTemplate<>(coreAdmin, sqsSettings, coreSettings.getOtaPackageTopic()); } @PreDestroy diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index 8806176f9f..cd1c34b612 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -131,13 +131,13 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new InMemoryTbQueueConsumer<>(coreSettings.getFirmwareTopic()); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new InMemoryTbQueueConsumer<>(coreSettings.getOtaPackageTopic()); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new InMemoryTbQueueProducer<>(coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new InMemoryTbQueueProducer<>(coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java index a526331116..c57260bef8 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaMonolithQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -277,24 +277,24 @@ public class KafkaMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEngi } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getFirmwareTopic()); - consumerBuilder.clientId("monolith-fw-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("monolith-fw-consumer"); - consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.clientId("monolith-ota-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId("monolith-ota-consumer"); + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); - requestBuilder.clientId("monolith-fw-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getFirmwareTopic()); + requestBuilder.clientId("monolith-ota-producer-" + serviceInfoProvider.getServiceId()); + requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java index 5f64e42ffd..bec01c7201 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/KafkaTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -245,24 +245,24 @@ public class KafkaTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + TbKafkaConsumerTemplate.TbKafkaConsumerTemplateBuilder> consumerBuilder = TbKafkaConsumerTemplate.builder(); consumerBuilder.settings(kafkaSettings); - consumerBuilder.topic(coreSettings.getFirmwareTopic()); - consumerBuilder.clientId("tb-core-fw-consumer-" + serviceInfoProvider.getServiceId()); - consumerBuilder.groupId("tb-core-fw-consumer"); - consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + consumerBuilder.topic(coreSettings.getOtaPackageTopic()); + consumerBuilder.clientId("tb-core-ota-consumer-" + serviceInfoProvider.getServiceId()); + consumerBuilder.groupId("tb-core-ota-consumer"); + consumerBuilder.decoder(msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); consumerBuilder.admin(fwUpdatesAdmin); consumerBuilder.statsService(consumerStatsService); return consumerBuilder.build(); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + TbKafkaProducerTemplate.TbKafkaProducerTemplateBuilder> requestBuilder = TbKafkaProducerTemplate.builder(); requestBuilder.settings(kafkaSettings); - requestBuilder.clientId("tb-core-fw-producer-" + serviceInfoProvider.getServiceId()); - requestBuilder.defaultTopic(coreSettings.getFirmwareTopic()); + requestBuilder.clientId("tb-core-ota-producer-" + serviceInfoProvider.getServiceId()); + requestBuilder.defaultTopic(coreSettings.getOtaPackageTopic()); requestBuilder.admin(fwUpdatesAdmin); return requestBuilder.build(); } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java index 3504c6aef2..3f014f578c 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubMonolithQueueFactory.java @@ -22,9 +22,9 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; -import org.thingsboard.server.gen.transport.TransportProtos.*; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -192,14 +192,14 @@ public class PubSubMonolithQueueFactory implements TbCoreQueueFactory, TbRuleEng } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java index a9ebf5a4de..65ea183d66 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/PubSubTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -166,14 +166,14 @@ public class PubSubTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbPubSubConsumerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbPubSubProducerTemplate<>(coreAdmin, pubSubSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java index cd6043cdde..7730f8f5dd 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqMonolithQueueFactory.java @@ -24,7 +24,7 @@ import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsRequest; import org.thingsboard.server.gen.js.JsInvokeProtos.RemoteJsResponse; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -190,14 +190,14 @@ public class RabbitMqMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java index 8b5ad00de4..c0abeab162 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/RabbitMqTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -172,14 +172,14 @@ public class RabbitMqTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbRabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbRabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java index b569d13742..2443093cf0 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusMonolithQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -189,14 +189,14 @@ public class ServiceBusMonolithQueueFactory implements TbCoreQueueFactory, TbRul } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java index 17e6eb9a27..7a9da236ed 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/ServiceBusTbCoreQueueFactory.java @@ -23,7 +23,7 @@ import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; @@ -172,14 +172,14 @@ public class ServiceBusTbCoreQueueFactory implements TbCoreQueueFactory { } @Override - public TbQueueConsumer> createToFirmwareStateServiceMsgConsumer() { - return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic(), - msg -> new TbProtoQueueMsg<>(msg.getKey(), ToFirmwareStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); + public TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer() { + return new TbServiceBusConsumerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic(), + msg -> new TbProtoQueueMsg<>(msg.getKey(), ToOtaPackageStateServiceMsg.parseFrom(msg.getData()), msg.getHeaders())); } @Override - public TbQueueProducer> createToFirmwareStateServiceMsgProducer() { - return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getFirmwareTopic()); + public TbQueueProducer> createToOtaPackageStateServiceMsgProducer() { + return new TbServiceBusProducerTemplate<>(coreAdmin, serviceBusSettings, coreSettings.getOtaPackageTopic()); } @Override diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java index be5ca967bf..584a6e389b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueFactory.java @@ -16,7 +16,7 @@ package org.thingsboard.server.queue.provider; import org.thingsboard.server.gen.js.JsInvokeProtos; -import org.thingsboard.server.gen.transport.TransportProtos.ToFirmwareStateServiceMsg; +import org.thingsboard.server.gen.transport.TransportProtos.ToOtaPackageStateServiceMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToRuleEngineMsg; @@ -91,14 +91,14 @@ public interface TbCoreQueueFactory extends TbUsageStatsClientQueueFactory { * * @return */ - TbQueueConsumer> createToFirmwareStateServiceMsgConsumer(); + TbQueueConsumer> createToOtaPackageStateServiceMsgConsumer(); /** * Used to consume messages about firmware update notifications by TB Core Service * * @return */ - TbQueueProducer> createToFirmwareStateServiceMsgProducer(); + TbQueueProducer> createToOtaPackageStateServiceMsgProducer(); /** * Used to consume high priority messages by TB Core Service diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java index b7e7a155ed..6c8ab4455b 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/TbCoreQueueProducerProvider.java @@ -15,7 +15,6 @@ */ package org.thingsboard.server.queue.provider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.stereotype.Service; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToCoreNotificationMsg; @@ -25,11 +24,12 @@ import org.thingsboard.server.gen.transport.TransportProtos.ToTransportMsg; import org.thingsboard.server.gen.transport.TransportProtos.ToUsageStatsServiceMsg; import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; +import org.thingsboard.server.queue.util.TbCoreComponent; import javax.annotation.PostConstruct; @Service -@ConditionalOnExpression("'${service.type:null}'=='monolith' || '${service.type:null}'=='tb-core'") +@TbCoreComponent public class TbCoreQueueProducerProvider implements TbQueueProducerProvider { private final TbCoreQueueFactory tbQueueProvider; diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java index d194fdb4ea..3c9ff5813e 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/settings/TbQueueCoreSettings.java @@ -26,8 +26,8 @@ public class TbQueueCoreSettings { @Value("${queue.core.topic}") private String topic; - @Value("${queue.core.firmware.topic:tb_firmware}") - private String firmwareTopic; + @Value("${queue.core.ota.topic:tb_ota_package}") + private String otaPackageTopic; @Value("${queue.core.usage-stats-topic:tb_usage_stats}") private String usageStatsTopic; diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index b4d929f60c..faf88c5620 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -388,7 +388,7 @@ enum ResponseStatus { FAILURE = 3; } -message GetFirmwareRequestMsg { +message GetOtaPackageRequestMsg { int64 deviceIdMSB = 1; int64 deviceIdLSB = 2; int64 tenantIdMSB = 3; @@ -396,10 +396,10 @@ message GetFirmwareRequestMsg { string type = 5; } -message GetFirmwareResponseMsg { +message GetOtaPackageResponseMsg { ResponseStatus responseStatus = 1; - int64 firmwareIdMSB = 2; - int64 firmwareIdLSB = 3; + int64 otaPackageIdMSB = 2; + int64 otaPackageIdLSB = 3; string type = 4; string title = 5; string version = 6; @@ -627,7 +627,7 @@ message TransportApiRequestMsg { ProvisionDeviceRequestMsg provisionDeviceRequestMsg = 7; ValidateDeviceLwM2MCredentialsRequestMsg validateDeviceLwM2MCredentialsRequestMsg = 8; GetResourceRequestMsg resourceRequestMsg = 9; - GetFirmwareRequestMsg firmwareRequestMsg = 10; + GetOtaPackageRequestMsg otaPackageRequestMsg = 10; GetSnmpDevicesRequestMsg snmpDevicesRequestMsg = 11; GetDeviceRequestMsg deviceRequestMsg = 12; GetDeviceCredentialsRequestMsg deviceCredentialsRequestMsg = 13; @@ -642,7 +642,7 @@ message TransportApiResponseMsg { GetSnmpDevicesResponseMsg snmpDevicesResponseMsg = 5; LwM2MResponseMsg lwM2MResponseMsg = 6; GetResourceResponseMsg resourceResponseMsg = 7; - GetFirmwareResponseMsg firmwareResponseMsg = 8; + GetOtaPackageResponseMsg otaPackageResponseMsg = 8; GetDeviceResponseMsg deviceResponseMsg = 9; GetDeviceCredentialsResponseMsg deviceCredentialsResponseMsg = 10; } @@ -710,13 +710,13 @@ message ToUsageStatsServiceMsg { int64 customerIdLSB = 7; } -message ToFirmwareStateServiceMsg { +message ToOtaPackageStateServiceMsg { int64 ts = 1; int64 tenantIdMSB = 2; int64 tenantIdLSB = 3; int64 deviceIdMSB = 4; int64 deviceIdLSB = 5; - int64 firmwareIdMSB = 6; - int64 firmwareIdLSB = 7; + int64 otaPackageIdMSB = 6; + int64 otaPackageIdLSB = 7; string type = 8; } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 47cf44f6fa..cd80aa42df 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -44,7 +44,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -350,10 +350,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { new CoapNoOpCallback(exchange)); break; case GET_FIRMWARE_REQUEST: - getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE); + getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.FIRMWARE); break; case GET_SOFTWARE_REQUEST: - getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE); + getOtaPackageCallback(sessionInfo, exchange, OtaPackageType.SOFTWARE); break; } } catch (AdaptorException e) { @@ -366,14 +366,14 @@ public class CoapTransportResource extends AbstractCoapTransportResource { return new UUID(sessionInfoProto.getSessionIdMSB(), sessionInfoProto.getSessionIdLSB()); } - private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) { - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + private void getOtaPackageCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, OtaPackageType firmwareType) { + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); + transportContext.getTransportService().process(sessionInfo, requestMsg, new OtaPackageCallback(exchange)); } private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { @@ -470,25 +470,25 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } - private class FirmwareCallback implements TransportServiceCallback { + private class OtaPackageCallback implements TransportServiceCallback { private final CoapExchange exchange; - FirmwareCallback(CoapExchange exchange) { + OtaPackageCallback(CoapExchange exchange) { this.exchange = exchange; } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg msg) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg msg) { String title = exchange.getQueryParameter("title"); String version = exchange.getQueryParameter("version"); if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { - String firmwareId = new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB()).toString(); + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); String strChunkSize = exchange.getQueryParameter("size"); String strChunk = exchange.getQueryParameter("chunk"); int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); - exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getFirmwareDataCache().get(firmwareId, chunkSize, chunk)); + exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); } else { exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 31180b478d..f4a3f705af 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -34,7 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.SessionMsgListener; @@ -213,7 +213,7 @@ public class DeviceApiController implements TbTransportService { @RequestParam(value = "version") String version, @RequestParam(value = "size", required = false, defaultValue = "0") int size, @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { - return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE); + return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.FIRMWARE); } @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) @@ -222,7 +222,7 @@ public class DeviceApiController implements TbTransportService { @RequestParam(value = "version") String version, @RequestParam(value = "size", required = false, defaultValue = "0") int size, @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { - return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE); + return getOtaPackageCallback(deviceToken, title, version, size, chunk, OtaPackageType.SOFTWARE); } @RequestMapping(value = "/provision", method = RequestMethod.POST) @@ -233,17 +233,17 @@ public class DeviceApiController implements TbTransportService { return responseWriter; } - private DeferredResult getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) { + private DeferredResult getOtaPackageCallback(String deviceToken, String title, String version, int size, int chunk, OtaPackageType firmwareType) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + TransportProtos.GetOtaPackageRequestMsg requestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setType(firmwareType.name()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk)); + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetOtaPackageCallback(responseWriter, title, version, size, chunk)); })); return responseWriter; } @@ -294,14 +294,14 @@ public class DeviceApiController implements TbTransportService { } } - private class GetFirmwareCallback implements TransportServiceCallback { + private class GetOtaPackageCallback implements TransportServiceCallback { private final DeferredResult responseWriter; private final String title; private final String version; private final int chuckSize; private final int chuck; - GetFirmwareCallback(DeferredResult responseWriter, String title, String version, int chuckSize, int chuck) { + GetOtaPackageCallback(DeferredResult responseWriter, String title, String version, int chuckSize, int chuck) { this.responseWriter = responseWriter; this.title = title; this.version = version; @@ -310,17 +310,17 @@ public class DeviceApiController implements TbTransportService { } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg firmwareResponseMsg) { - if (!TransportProtos.ResponseStatus.SUCCESS.equals(firmwareResponseMsg.getResponseStatus())) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg otaPackageResponseMsg) { + if (!TransportProtos.ResponseStatus.SUCCESS.equals(otaPackageResponseMsg.getResponseStatus())) { responseWriter.setResult(new ResponseEntity<>(HttpStatus.NOT_FOUND)); - } else if (title.equals(firmwareResponseMsg.getTitle()) && version.equals(firmwareResponseMsg.getVersion())) { - String firmwareId = new UUID(firmwareResponseMsg.getFirmwareIdMSB(), firmwareResponseMsg.getFirmwareIdLSB()).toString(); - ByteArrayResource resource = new ByteArrayResource(transportContext.getFirmwareDataCache().get(firmwareId, chuckSize, chuck)); + } else if (title.equals(otaPackageResponseMsg.getTitle()) && version.equals(otaPackageResponseMsg.getVersion())) { + String otaPackageId = new UUID(otaPackageResponseMsg.getOtaPackageIdMSB(), otaPackageResponseMsg.getOtaPackageIdLSB()).toString(); + ByteArrayResource resource = new ByteArrayResource(transportContext.getOtaPackageDataCache().get(otaPackageId, chuckSize, chuck)); ResponseEntity response = ResponseEntity.ok() - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + firmwareResponseMsg.getFileName()) - .header("x-filename", firmwareResponseMsg.getFileName()) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackageResponseMsg.getFileName()) + .header("x-filename", otaPackageResponseMsg.getFileName()) .contentLength(resource.contentLength()) - .contentType(parseMediaType(firmwareResponseMsg.getContentType())) + .contentType(parseMediaType(otaPackageResponseMsg.getContentType())) .body(resource); responseWriter.setResult(response); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 9092f8abeb..abe2a50855 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -39,13 +39,13 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.firmware.FirmwareKey; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -87,8 +87,8 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED; @@ -132,7 +132,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler private final TransportService transportService; private final LwM2mTransportContext context; public final LwM2MTransportServerConfig config; - public final FirmwareDataCache firmwareDataCache; + public final OtaPackageDataCache otaPackageDataCache; public final LwM2mTransportServerHelper helper; private final LwM2MJsonAdaptor adaptor; private final TbLwM2MDtlsSessionStore sessionStore; @@ -143,14 +143,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @Lazy LwM2mTransportRequest lwM2mTransportRequest, - FirmwareDataCache firmwareDataCache, + OtaPackageDataCache otaPackageDataCache, LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { this.transportService = transportService; this.config = config; this.helper = helper; this.clientContext = clientContext; this.lwM2mTransportRequest = lwM2mTransportRequest; - this.firmwareDataCache = firmwareDataCache; + this.otaPackageDataCache = otaPackageDataCache; this.context = context; this.adaptor = adaptor; this.rpcSubscriptions = new ConcurrentHashMap<>(); @@ -357,14 +357,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if ((FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) + if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) - || (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) + || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { this.getInfoFirmwareUpdate(lwM2MClient); - } else if ((FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName) + } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) - || (FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName) + || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { this.getInfoSoftwareUpdate(lwM2MClient); } @@ -391,7 +391,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); Object valueNew = getValueFromKvProto(tsKvProto.getKv()); - if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { + if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); } }); @@ -1344,18 +1344,18 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { - transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.FIRMWARE.name()), + transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), new TransportServiceCallback<>() { @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(FirmwareType.FIRMWARE.name())) { + && response.getType().equals(OtaPackageType.FIRMWARE.name())) { lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); + lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } else { - log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); + log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); } } @@ -1373,15 +1373,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { DefaultLwM2MTransportMsgHandler serviceImpl = this; - transportService.process(sessionInfo, createFirmwareRequestMsg(sessionInfo, FirmwareType.SOFTWARE.name()), + transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), new TransportServiceCallback<>() { @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) - && response.getType().equals(FirmwareType.SOFTWARE.name())) { + && response.getType().equals(OtaPackageType.SOFTWARE.name())) { lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); + lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); @@ -1397,8 +1397,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - private TransportProtos.GetFirmwareRequestMsg createFirmwareRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { - return TransportProtos.GetFirmwareRequestMsg.newBuilder() + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB()) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index e5cd289758..87e72c2873 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -71,8 +71,8 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 320059b0a7..a013f75c1b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -43,10 +43,10 @@ import org.eclipse.leshan.server.registration.Registration; import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareKey; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; -import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.ota.OtaPackageKey; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; @@ -77,12 +77,12 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.DOWNLOADING; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.FAILED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATED; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.VERIFIED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; @@ -139,7 +139,7 @@ public class LwM2mTransportUtil { public static final String ERROR_KEY = "error"; public static final String METHOD_KEY = "methodName"; - // FirmWare + // Firmware public static final String FW_UPDATE = "Firmware update"; public static final Integer FW_ID = 5; // Package W @@ -155,7 +155,7 @@ public class LwM2mTransportUtil { // Update E public static final String FW_UPDATE_ID = "/5/0/2"; - // SoftWare + // Software public static final String SW_UPDATE = "Software update"; public static final Integer SW_ID = 9; // Package W @@ -354,7 +354,7 @@ public class LwM2mTransportUtil { * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static FirmwareUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { + public static OtaPackageUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { switch (updateResultFw) { case INITIAL: switch (stateFw) { @@ -500,7 +500,7 @@ public class LwM2mTransportUtil { * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static FirmwareUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { + public static OtaPackageUpdateStatus EqualsSwSateToFirmwareUpdateStatus(UpdateStateSw updateStateSw, UpdateResultSw updateResultSw) { switch (updateResultSw) { case INITIAL: switch (updateStateSw) { @@ -932,15 +932,15 @@ public class LwM2mTransportUtil { } public static boolean isFwSwWords (String pathName) { - return FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.TITLE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.CHECKSUM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.CHECKSUM_ALGORITHM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.SIZE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.VERSION).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.TITLE).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.CHECKSUM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.CHECKSUM_ALGORITHM).equals(pathName) - || FirmwareUtil.getAttributeKey(FirmwareType.SOFTWARE, FirmwareKey.SIZE).equals(pathName); + return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.SIZE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) + || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 9ef45c3b3b..a6a1fadbf3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -31,7 +31,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; @@ -120,8 +120,8 @@ public class LwM2mClient implements Cloneable { this.init = false; this.queuedRequests = new ConcurrentLinkedQueue<>(); - this.fwUpdate = new LwM2mFwSwUpdate(this, FirmwareType.FIRMWARE); - this.swUpdate = new LwM2mFwSwUpdate(this, FirmwareType.SOFTWARE); + this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE); + this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE); if (this.credentials != null && this.credentials.hasDeviceInfo()) { this.session = createSession(nodeId, sessionId, credentials); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index e3e8608839..a0e5432c5a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -20,8 +20,8 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.request.ContentFormat; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest; @@ -32,11 +32,11 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; -import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; -import static org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; @@ -97,14 +97,14 @@ public class LwM2mFwSwUpdate { @Getter @Setter private volatile boolean infoFwSwUpdate = false; - private final FirmwareType type; + private final OtaPackageType type; @Getter LwM2mClient lwM2MClient; @Getter @Setter private final List pendingInfoRequestsStart; - public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, FirmwareType type) { + public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) { this.lwM2MClient = lwM2MClient; this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.type = type; @@ -139,7 +139,7 @@ public class LwM2mFwSwUpdate { } if (this.pendingInfoRequestsStart.size() == 0) { this.infoFwSwUpdate = false; - if (!FirmwareUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { + if (!OtaPackageUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() : this.conditionalSwUpdateStart(); if (conditionalStart) { @@ -154,12 +154,12 @@ public class LwM2mFwSwUpdate { * before operation Write: fw_state = DOWNLOADING */ private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { - this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name(); + this.stateUpdate = OtaPackageUpdateStatus.DOWNLOADING.name(); // this.observeStateUpdate(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); int chunkSize = 0; int chunk = 0; - byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk); + byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), firmwareChunk, handler.config.getTimeout(), null); @@ -287,10 +287,10 @@ public class LwM2mFwSwUpdate { LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); if (success) { - this.stateUpdate = FirmwareUpdateStatus.UPDATED.name(); + this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); } else { - this.stateUpdate = FirmwareUpdateStatus.FAILED.name(); + this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value); } handler.helper.sendParametersOnThingsboardTelemetry( diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index f2a6922b6d..961c20ed34 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -47,8 +47,8 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; import org.thingsboard.server.common.transport.SessionMsgListener; @@ -126,8 +126,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement private volatile InetSocketAddress address; private volatile GatewaySessionHandler gatewaySessionHandler; - private final ConcurrentHashMap fwSessions; - private final ConcurrentHashMap fwChunkSizes; + private final ConcurrentHashMap otaPackSessions; + private final ConcurrentHashMap chunkSizes; MqttTransportHandler(MqttTransportContext context, SslHandler sslHandler) { this.sessionId = UUID.randomUUID(); @@ -137,8 +137,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement this.sslHandler = sslHandler; this.mqttQoSMap = new ConcurrentHashMap<>(); this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap, context); - this.fwSessions = new ConcurrentHashMap<>(); - this.fwChunkSizes = new ConcurrentHashMap<>(); + this.otaPackSessions = new ConcurrentHashMap<>(); + this.chunkSizes = new ConcurrentHashMap<>(); } @Override @@ -320,9 +320,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) { - getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE); + getOtaPackageCallback(ctx, mqttMsg, msgId, fwMatcher, OtaPackageType.FIRMWARE); } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) { - getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE); + getOtaPackageCallback(ctx, mqttMsg, msgId, fwMatcher, OtaPackageType.SOFTWARE); } else { transportService.reportActivity(deviceSessionCtx.getSessionInfo()); ack(ctx, msgId); @@ -334,38 +334,38 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) { + private void getOtaPackageCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, OtaPackageType type) { String payload = mqttMsg.content().toString(UTF8); int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; String requestId = fwMatcher.group("requestId"); int chunk = Integer.parseInt(fwMatcher.group("chunk")); if (chunkSize > 0) { - this.fwChunkSizes.put(requestId, chunkSize); + this.chunkSizes.put(requestId, chunkSize); } else { - chunkSize = fwChunkSizes.getOrDefault(requestId, 0); + chunkSize = chunkSizes.getOrDefault(requestId, 0); } if (chunkSize > context.getMaxPayloadSize()) { - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); + sendOtaPackageError(ctx, PAYLOAD_TOO_LARGE); return; } - String firmwareId = fwSessions.get(requestId); + String otaPackageId = otaPackSessions.get(requestId); - if (firmwareId != null) { - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type); + if (otaPackageId != null) { + sendOtaPackage(ctx, mqttMsg.variableHeader().packetId(), otaPackageId, requestId, chunkSize, chunk, type); } else { TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + TransportProtos.GetOtaPackageRequestMsg getOtaPackageRequestMsg = TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setType(type.name()) .build(); - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, - new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk)); + transportService.process(deviceSessionCtx.getSessionInfo(), getOtaPackageRequestMsg, + new OtaPackageCallback(ctx, msgId, getOtaPackageRequestMsg, requestId, chunkSize, chunk)); } } @@ -425,15 +425,15 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private class FirmwareCallback implements TransportServiceCallback { + private class OtaPackageCallback implements TransportServiceCallback { private final ChannelHandlerContext ctx; private final int msgId; - private final TransportProtos.GetFirmwareRequestMsg msg; + private final TransportProtos.GetOtaPackageRequestMsg msg; private final String requestId; private final int chunkSize; private final int chunk; - FirmwareCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.GetFirmwareRequestMsg msg, String requestId, int chunkSize, int chunk) { + OtaPackageCallback(ChannelHandlerContext ctx, int msgId, TransportProtos.GetOtaPackageRequestMsg msg, String requestId, int chunkSize, int chunk) { this.ctx = ctx; this.msgId = msgId; this.msg = msg; @@ -443,13 +443,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } @Override - public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { - FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); - fwSessions.put(requestId, firmwareId.toString()); - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType())); + OtaPackageId firmwareId = new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); + otaPackSessions.put(requestId, firmwareId.toString()); + sendOtaPackage(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, OtaPackageType.valueOf(response.getType())); } else { - sendFirmwareError(ctx, response.getResponseStatus().toString()); + sendOtaPackageError(ctx, response.getResponseStatus().toString()); } } @@ -460,11 +460,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) { + private void sendOtaPackage(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, OtaPackageType type) { log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); ack(ctx, msgId); try { - byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); + byte[] firmwareChunk = context.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk); deviceSessionCtx.getPayloadAdaptor() .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type) .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); @@ -473,7 +473,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void sendFirmwareError(ChannelHandlerContext ctx, String error) { + private void sendOtaPackageError(ChannelHandlerContext ctx, String error) { log.warn("[{}] {}", sessionId, error); deviceSessionCtx.getChannel().writeAndFlush(deviceSessionCtx .getPayloadAdaptor() diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index a9d7b3e2ea..19d2e15fc5 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -30,7 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; @@ -155,7 +155,7 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) { + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) { return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index d0c1f30524..62ae6ae027 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -23,7 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; @@ -78,7 +78,7 @@ public interface MqttTransportAdaptor { Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; - Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException; default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { MqttFixedHeader mqttFixedHeader = diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java index 08a2f9abe3..a007c73a5a 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java @@ -28,7 +28,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.ProtoConverter; @@ -168,7 +168,7 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException { + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, OtaPackageType firmwareType) throws AdaptorException { return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_SOFTWARE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java index d4798108c6..d48d3fd04e 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportContext.java @@ -21,14 +21,13 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.common.util.ThingsBoardExecutors; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; import org.thingsboard.server.queue.scheduler.SchedulerComponent; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; /** * Created by ashvayka on 15.10.18. @@ -53,7 +52,7 @@ public abstract class TransportContext { @Getter @Autowired - private FirmwareDataCache firmwareDataCache; + private OtaPackageDataCache otaPackageDataCache; @Autowired private TransportResourceCache transportResourceCache; diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java index 2209ffc305..4a4e68f64f 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/TransportService.java @@ -29,8 +29,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetEntityProfileResponseMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareRequestMsg; -import org.thingsboard.server.gen.transport.TransportProtos.GetFirmwareResponseMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageRequestMsg; +import org.thingsboard.server.gen.transport.TransportProtos.GetOtaPackageResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetOrCreateDeviceFromGatewayRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetResourceRequestMsg; import org.thingsboard.server.gen.transport.TransportProtos.GetResourceResponseMsg; @@ -115,7 +115,7 @@ public interface TransportService { void process(TransportToDeviceActorMsg msg, TransportServiceCallback callback); - void process(SessionInfoProto sessionInfoProto, GetFirmwareRequestMsg msg, TransportServiceCallback callback); + void process(SessionInfoProto sessionInfoProto, GetOtaPackageRequestMsg msg, TransportServiceCallback callback); SessionMetaData registerAsyncSession(SessionInfoProto sessionInfo, SessionMsgListener listener); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 7035c3374e..77b75dc07a 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -614,13 +614,13 @@ public class DefaultTransportService implements TransportService { } @Override - public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetFirmwareRequestMsg msg, TransportServiceCallback callback) { + public void process(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.GetOtaPackageRequestMsg msg, TransportServiceCallback callback) { if (checkLimits(sessionInfo, msg, callback)) { TbProtoQueueMsg protoMsg = - new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setFirmwareRequestMsg(msg).build()); + new TbProtoQueueMsg<>(UUID.randomUUID(), TransportProtos.TransportApiRequestMsg.newBuilder().setOtaPackageRequestMsg(msg).build()); AsyncCallbackTemplate.withCallback(transportApiRequestTemplate.send(protoMsg), response -> { - callback.onSuccess(response.getValue().getFirmwareResponseMsg()); + callback.onSuccess(response.getValue().getOtaPackageResponseMsg()); }, callback::onError, transportCallbackExecutor); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index 34982b92b6..de13c85727 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -21,6 +21,7 @@ import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; @@ -81,9 +82,12 @@ public interface DeviceDao extends Dao, TenantEntityDao { */ PageData findDevicesByTenantIdAndType(UUID tenantId, String type, PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(UUID tenantId, + UUID deviceProfileId, + OtaPackageType type, + PageLink pageLink); - PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink); + Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(UUID tenantId, UUID deviceProfileId, OtaPackageType otaPackageType); /** * Find device infos by tenantId, type and page link. diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index 8158134925..e41289e97b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -43,7 +43,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceProfileProvisionType; import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; @@ -57,7 +57,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -66,7 +66,7 @@ import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -119,7 +119,7 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D private CacheManager cacheManager; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Autowired private RuleChainService ruleChainService; @@ -427,11 +427,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } if (deviceProfile.getFirmwareId() != null) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId()); + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getFirmwareId()); if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } - if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); } if (firmware.getData() == null) { @@ -443,11 +443,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D } if (deviceProfile.getSoftwareId() != null) { - Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId()); + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, deviceProfile.getSoftwareId()); if (software == null) { throw new DataValidationException("Can't assign non-existent software!"); } - if (!software.getType().equals(FirmwareType.SOFTWARE)) { + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { throw new DataValidationException("Can't assign software with type: " + software.getType()); } if (software.getData() == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 237d53a7f2..34aa462a6d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -41,7 +41,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityView; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.device.DeviceSearchQuery; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; @@ -54,13 +54,13 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.edge.Edge; -import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.relation.EntityRelation; @@ -76,7 +76,7 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponseStatus; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.tenant.TbTenantProfileCache; @@ -138,7 +138,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe private TbTenantProfileCache tenantProfileCache; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public DeviceInfo findDeviceInfoById(TenantId tenantId, DeviceId deviceId) { @@ -201,14 +201,12 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe deviceCredentials.setDeviceId(savedDevice.getId()); if (device.getId() == null) { deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); - } - else { + } else { DeviceCredentials foundDeviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), savedDevice.getId()); if (foundDeviceCredentials == null) { deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials); - } - else { - deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); + } else { + deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials); } } return savedDevice; @@ -364,21 +362,24 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + public PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(TenantId tenantId, + DeviceProfileId deviceProfileId, + OtaPackageType type, + PageLink pageLink) { + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}], pageLink [{}]", + tenantId, deviceProfileId, type, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); + validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); validatePageLink(pageLink); - return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink); + return deviceDao.findDevicesByTenantIdAndTypeAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type, pageLink); } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType type) { + log.trace("Executing countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage, tenantId [{}], deviceProfileId [{}], type [{}]", tenantId, deviceProfileId, type); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validateString(type, "Incorrect type " + type); - validatePageLink(pageLink); - return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink); + validateId(tenantId, INCORRECT_DEVICE_PROFILE_ID + deviceProfileId); + return deviceDao.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(tenantId.getId(), deviceProfileId.getId(), type); } @Override @@ -708,11 +709,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe .ifPresent(DeviceTransportConfiguration::validate); if (device.getFirmwareId() != null) { - Firmware firmware = firmwareService.findFirmwareById(tenantId, device.getFirmwareId()); + OtaPackage firmware = otaPackageService.findOtaPackageById(tenantId, device.getFirmwareId()); if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } - if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + if (!firmware.getType().equals(OtaPackageType.FIRMWARE)) { throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); } if (firmware.getData() == null) { @@ -724,11 +725,11 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe } if (device.getSoftwareId() != null) { - Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId()); + OtaPackage software = otaPackageService.findOtaPackageById(tenantId, device.getSoftwareId()); if (software == null) { throw new DataValidationException("Can't assign non-existent software!"); } - if (!software.getType().equals(FirmwareType.SOFTWARE)) { + if (!software.getType().equals(OtaPackageType.SOFTWARE)) { throw new DataValidationException("Can't assign software with type: " + software.getType()); } if (software.getData() == null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java index 73cebc0797..7686d9ec21 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/entity/BaseEntityService.java @@ -32,7 +32,7 @@ import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; @@ -49,7 +49,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -102,7 +102,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe private ResourceService resourceService; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public void deleteEntityRelations(TenantId tenantId, EntityId entityId) { @@ -167,8 +167,8 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case TB_RESOURCE: hasName = resourceService.findResourceInfoByIdAsync(tenantId, new TbResourceId(entityId.getId())); break; - case FIRMWARE: - hasName = firmwareService.findFirmwareInfoByIdAsync(tenantId, new FirmwareId(entityId.getId())); + case OTA_PACKAGE: + hasName = otaPackageService.findOtaPackageInfoByIdAsync(tenantId, new OtaPackageId(entityId.getId())); break; default: throw new IllegalStateException("Not Implemented!"); @@ -192,7 +192,7 @@ public class BaseEntityService extends AbstractEntityService implements EntitySe case DEVICE_PROFILE: case API_USAGE_STATE: case TB_RESOURCE: - case FIRMWARE: + case OTA_PACKAGE: break; case CUSTOMER: hasCustomerId = () -> new CustomerId(entityId.getId()); diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java deleted file mode 100644 index 2e0d5c03b6..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.firmware; - -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; -import com.google.common.util.concurrent.ListenableFuture; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.hibernate.exception.ConstraintViolationException; -import org.springframework.cache.Cache; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.thingsboard.server.cache.firmware.FirmwareDataCache; -import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; -import org.thingsboard.server.common.data.exception.ThingsboardException; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.device.DeviceProfileDao; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.service.DataValidator; -import org.thingsboard.server.dao.service.PaginatedRemover; -import org.thingsboard.server.dao.tenant.TenantDao; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import static org.thingsboard.server.common.data.CacheConstants.FIRMWARE_CACHE; -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validatePageLink; - -@Service -@Slf4j -@RequiredArgsConstructor -public class BaseFirmwareService implements FirmwareService { - public static final String INCORRECT_FIRMWARE_ID = "Incorrect firmwareId "; - public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - - private final TenantDao tenantDao; - private final DeviceProfileDao deviceProfileDao; - private final FirmwareDao firmwareDao; - private final FirmwareInfoDao firmwareInfoDao; - private final CacheManager cacheManager; - private final FirmwareDataCache firmwareDataCache; - - @Override - public FirmwareInfo saveFirmwareInfo(FirmwareInfo firmwareInfo) { - log.trace("Executing saveFirmwareInfo [{}]", firmwareInfo); - firmwareInfoValidator.validate(firmwareInfo, FirmwareInfo::getTenantId); - try { - FirmwareId firmwareId = firmwareInfo.getId(); - if (firmwareId != null) { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - } - return firmwareInfoDao.save(firmwareInfo.getTenantId(), firmwareInfo); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { - throw new DataValidationException("Firmware with such title and version already exists!"); - } else { - throw t; - } - } - } - - @Override - public Firmware saveFirmware(Firmware firmware) { - log.trace("Executing saveFirmware [{}]", firmware); - firmwareValidator.validate(firmware, FirmwareInfo::getTenantId); - try { - FirmwareId firmwareId = firmware.getId(); - if (firmwareId != null) { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - } - return firmwareDao.save(firmware.getTenantId(), firmware); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("firmware_tenant_title_version_unq_key")) { - throw new DataValidationException("Firmware with such title and version already exists!"); - } else { - throw t; - } - } - } - - @Override - public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) { - if (data == null || !data.hasArray() || data.array().length == 0) { - throw new DataValidationException("Firmware data should be specified!"); - } - - return getHashFunction(checksumAlgorithm).hashBytes(data.array()).toString(); - } - - private HashFunction getHashFunction(ChecksumAlgorithm checksumAlgorithm) { - switch (checksumAlgorithm) { - case MD5: - return Hashing.md5(); - case SHA256: - return Hashing.sha256(); - case SHA384: - return Hashing.sha384(); - case SHA512: - return Hashing.sha512(); - case CRC32: - return Hashing.crc32(); - case MURMUR3_32: - return Hashing.murmur3_32(); - case MURMUR3_128: - return Hashing.murmur3_128(); - default: - throw new DataValidationException("Unknown checksum algorithm!"); - } - } - - @Override - public Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareById [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareDao.findById(tenantId, firmwareId.getId()); - } - - @Override - @Cacheable(cacheNames = FIRMWARE_CACHE, key = "{#firmwareId}") - public FirmwareInfo findFirmwareInfoById(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareInfoById [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareInfoDao.findById(tenantId, firmwareId.getId()); - } - - @Override - public ListenableFuture findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); - } - - @Override - public PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { - log.trace("Executing findTenantFirmwaresByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validatePageLink(pageLink); - return firmwareInfoDao.findFirmwareInfoByTenantId(tenantId, pageLink); - } - - @Override - public PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { - log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - validatePageLink(pageLink); - return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink); - } - - @Override - public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) { - log.trace("Executing deleteFirmware [{}]", firmwareId); - validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - try { - Cache cache = cacheManager.getCache(FIRMWARE_CACHE); - cache.evict(toFirmwareInfoKey(firmwareId)); - firmwareDataCache.evict(firmwareId.toString()); - firmwareDao.removeById(tenantId, firmwareId.getId()); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) { - throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { - throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { - throw new DataValidationException("The software referenced by the devices cannot be deleted!"); - } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { - throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); - } else { - throw t; - } - } - } - - @Override - public void deleteFirmwaresByTenantId(TenantId tenantId) { - log.trace("Executing deleteFirmwaresByTenantId, tenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - tenantFirmwareRemover.removeEntities(tenantId, tenantId); - } - - private DataValidator firmwareInfoValidator = new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) { - validateImpl(firmwareInfo); - } - - @Override - protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { - FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); - - validateUpdateDeviceProfile(firmware, firmwareOld); - BaseFirmwareService.validateUpdate(firmware, firmwareOld); - } - }; - - private DataValidator firmwareValidator = new DataValidator<>() { - - @Override - protected void validateDataImpl(TenantId tenantId, Firmware firmware) { - validateImpl(firmware); - - if (StringUtils.isEmpty(firmware.getFileName())) { - throw new DataValidationException("Firmware file name should be specified!"); - } - - if (StringUtils.isEmpty(firmware.getContentType())) { - throw new DataValidationException("Firmware content type should be specified!"); - } - - if (firmware.getChecksumAlgorithm() == null) { - throw new DataValidationException("Firmware checksum algorithm should be specified!"); - } - if (StringUtils.isEmpty(firmware.getChecksum())) { - throw new DataValidationException("Firmware checksum should be specified!"); - } - - String currentChecksum; - - currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData()); - - if (!currentChecksum.equals(firmware.getChecksum())) { - throw new DataValidationException("Wrong firmware file!"); - } - } - - @Override - protected void validateUpdate(TenantId tenantId, Firmware firmware) { - Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); - - validateUpdateDeviceProfile(firmware, firmwareOld); - BaseFirmwareService.validateUpdate(firmware, firmwareOld); - - if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { - throw new DataValidationException("Updating firmware data is prohibited!"); - } - } - }; - - private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) { - if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) { - if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) { - throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!"); - } - } - } - - private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { - if (!firmwareOld.getType().equals(firmware.getType())) { - throw new DataValidationException("Updating type is prohibited!"); - } - - if (!firmwareOld.getTitle().equals(firmware.getTitle())) { - throw new DataValidationException("Updating firmware title is prohibited!"); - } - - if (!firmwareOld.getVersion().equals(firmware.getVersion())) { - throw new DataValidationException("Updating firmware version is prohibited!"); - } - - if (firmwareOld.getFileName() != null && !firmwareOld.getFileName().equals(firmware.getFileName())) { - throw new DataValidationException("Updating firmware file name is prohibited!"); - } - - if (firmwareOld.getContentType() != null && !firmwareOld.getContentType().equals(firmware.getContentType())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getChecksumAlgorithm() != null && !firmwareOld.getChecksumAlgorithm().equals(firmware.getChecksumAlgorithm())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getChecksum() != null && !firmwareOld.getChecksum().equals(firmware.getChecksum())) { - throw new DataValidationException("Updating firmware content type is prohibited!"); - } - - if (firmwareOld.getDataSize() != null && !firmwareOld.getDataSize().equals(firmware.getDataSize())) { - throw new DataValidationException("Updating firmware data size is prohibited!"); - } - } - - private void validateImpl(FirmwareInfo firmwareInfo) { - if (firmwareInfo.getTenantId() == null) { - throw new DataValidationException("Firmware should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); - } - } - - if (firmwareInfo.getDeviceProfileId() != null) { - DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId()); - if (deviceProfile == null) { - throw new DataValidationException("Firmware is referencing to non-existent device profile!"); - } - } - - if (firmwareInfo.getType() == null) { - throw new DataValidationException("Type should be specified!"); - } - - if (StringUtils.isEmpty(firmwareInfo.getTitle())) { - throw new DataValidationException("Firmware title should be specified!"); - } - - if (StringUtils.isEmpty(firmwareInfo.getVersion())) { - throw new DataValidationException("Firmware version should be specified!"); - } - } - - private PaginatedRemover tenantFirmwareRemover = - new PaginatedRemover<>() { - - @Override - protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { - return firmwareInfoDao.findFirmwareInfoByTenantId(id, pageLink); - } - - @Override - protected void removeEntity(TenantId tenantId, FirmwareInfo entity) { - deleteFirmware(tenantId, entity.getId()); - } - }; - - protected Optional extractConstraintViolationException(Exception t) { - if (t instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) t); - } else if (t.getCause() instanceof ConstraintViolationException) { - return Optional.of((ConstraintViolationException) (t.getCause())); - } else { - return Optional.empty(); - } - } - - private static List toFirmwareInfoKey(FirmwareId firmwareId) { - return Collections.singletonList(firmwareId); - } - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 0bc346e36c..34878a3810 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -479,22 +479,21 @@ public class ModelConstants { public static final String RESOURCE_DATA_COLUMN = "data"; /** - * Firmware constants. - */ - public static final String FIRMWARE_TABLE_NAME = "firmware"; - public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; - public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; - public static final String FIRMWARE_TYPE_COLUMN = "type"; - public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; - public static final String FIRMWARE_VERSION_COLUMN = "version"; - public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; - public static final String FIRMWARE_CONTENT_TYPE_COLUMN = "content_type"; - public static final String FIRMWARE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm"; - public static final String FIRMWARE_CHECKSUM_COLUMN = "checksum"; - public static final String FIRMWARE_DATA_COLUMN = "data"; - public static final String FIRMWARE_DATA_SIZE_COLUMN = "data_size"; - public static final String FIRMWARE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; - public static final String FIRMWARE_HAS_DATA_PROPERTY = "has_data"; + * Ota Package constants. + */ + public static final String OTA_PACKAGE_TABLE_NAME = "ota_package"; + public static final String OTA_PACKAGE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; + public static final String OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; + public static final String OTA_PACKAGE_TYPE_COLUMN = "type"; + public static final String OTA_PACKAGE_TILE_COLUMN = TITLE_PROPERTY; + public static final String OTA_PACKAGE_VERSION_COLUMN = "version"; + public static final String OTA_PACKAGE_FILE_NAME_COLUMN = "file_name"; + public static final String OTA_PACKAGE_CONTENT_TYPE_COLUMN = "content_type"; + public static final String OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN = "checksum_algorithm"; + public static final String OTA_PACKAGE_CHECKSUM_COLUMN = "checksum"; + public static final String OTA_PACKAGE_DATA_COLUMN = "data"; + public static final String OTA_PACKAGE_DATA_SIZE_COLUMN = "data_size"; + public static final String OTA_PACKAGE_ADDITIONAL_INFO_COLUMN = ADDITIONAL_INFO_PROPERTY; /** * Edge constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index ed6a871fea..c9913565e4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -28,7 +28,7 @@ import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -154,10 +154,10 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti device.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); } if (firmwareId != null) { - device.setFirmwareId(new FirmwareId(firmwareId)); + device.setFirmwareId(new OtaPackageId(firmwareId)); } if (softwareId != null) { - device.setSoftwareId(new FirmwareId(softwareId)); + device.setSoftwareId(new OtaPackageId(softwareId)); } device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); device.setName(name); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index ec440526a4..a3d6c491c0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -29,7 +29,7 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -178,11 +178,11 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl deviceProfile.setProvisionDeviceKey(provisionDeviceKey); if (firmwareId != null) { - deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); + deviceProfile.setFirmwareId(new OtaPackageId(firmwareId)); } if (softwareId != null) { - deviceProfile.setSoftwareId(new FirmwareId(softwareId)); + deviceProfile.setSoftwareId(new OtaPackageId(softwareId)); } return deviceProfile; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java similarity index 62% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java index e16d0417e4..97e4dbebbd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageEntity.java @@ -20,11 +20,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -40,75 +40,75 @@ import javax.persistence.Table; import java.nio.ByteBuffer; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CONTENT_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = FIRMWARE_TABLE_NAME) -public class FirmwareEntity extends BaseSqlEntity implements SearchTextEntity { +@Table(name = OTA_PACKAGE_TABLE_NAME) +public class OtaPackageEntity extends BaseSqlEntity implements SearchTextEntity { - @Column(name = FIRMWARE_TENANT_ID_COLUMN) + @Column(name = OTA_PACKAGE_TENANT_ID_COLUMN) private UUID tenantId; - @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + @Column(name = OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN) private UUID deviceProfileId; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_TYPE_COLUMN) - private FirmwareType type; + @Column(name = OTA_PACKAGE_TYPE_COLUMN) + private OtaPackageType type; - @Column(name = FIRMWARE_TITLE_COLUMN) + @Column(name = OTA_PACKAGE_TILE_COLUMN) private String title; - @Column(name = FIRMWARE_VERSION_COLUMN) + @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; - @Column(name = FIRMWARE_FILE_NAME_COLUMN) + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; - @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN) + @Column(name = OTA_PACKAGE_CONTENT_TYPE_COLUMN) private String contentType; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN) private ChecksumAlgorithm checksumAlgorithm; - @Column(name = FIRMWARE_CHECKSUM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_COLUMN) private String checksum; @Lob - @Column(name = FIRMWARE_DATA_COLUMN, columnDefinition = "BINARY") + @Column(name = OTA_PACKAGE_DATA_COLUMN, columnDefinition = "BINARY") private byte[] data; - @Column(name = FIRMWARE_DATA_SIZE_COLUMN) + @Column(name = OTA_PACKAGE_DATA_SIZE_COLUMN) private Long dataSize; @Type(type = "json") - @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) + @Column(name = ModelConstants.OTA_PACKAGE_ADDITIONAL_INFO_COLUMN) private JsonNode additionalInfo; @Column(name = SEARCH_TEXT_PROPERTY) private String searchText; - public FirmwareEntity() { + public OtaPackageEntity() { super(); } - public FirmwareEntity(Firmware firmware) { + public OtaPackageEntity(OtaPackage firmware) { this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); @@ -138,8 +138,8 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex } @Override - public Firmware toData() { - Firmware firmware = new Firmware(new FirmwareId(id)); + public OtaPackage toData() { + OtaPackage firmware = new OtaPackage(new OtaPackageId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); if (deviceProfileId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java similarity index 63% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java index 93a6e83f25..30441ed098 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java @@ -21,11 +21,11 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; @@ -40,60 +40,60 @@ import javax.persistence.Table; import javax.persistence.Transient; import java.util.UUID; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CHECKSUM_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_CONTENT_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_FILE_NAME_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_NAME; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @Data @EqualsAndHashCode(callSuper = true) @Entity @TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = FIRMWARE_TABLE_NAME) -public class FirmwareInfoEntity extends BaseSqlEntity implements SearchTextEntity { +@Table(name = OTA_PACKAGE_TABLE_NAME) +public class OtaPackageInfoEntity extends BaseSqlEntity implements SearchTextEntity { - @Column(name = FIRMWARE_TENANT_ID_COLUMN) + @Column(name = OTA_PACKAGE_TENANT_ID_COLUMN) private UUID tenantId; - @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + @Column(name = OTA_PACKAGE_DEVICE_PROFILE_ID_COLUMN) private UUID deviceProfileId; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_TYPE_COLUMN) - private FirmwareType type; + @Column(name = OTA_PACKAGE_TYPE_COLUMN) + private OtaPackageType type; - @Column(name = FIRMWARE_TITLE_COLUMN) + @Column(name = OTA_PACKAGE_TILE_COLUMN) private String title; - @Column(name = FIRMWARE_VERSION_COLUMN) + @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; - @Column(name = FIRMWARE_FILE_NAME_COLUMN) + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; - @Column(name = FIRMWARE_CONTENT_TYPE_COLUMN) + @Column(name = OTA_PACKAGE_CONTENT_TYPE_COLUMN) private String contentType; @Enumerated(EnumType.STRING) - @Column(name = FIRMWARE_CHECKSUM_ALGORITHM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_ALGORITHM_COLUMN) private ChecksumAlgorithm checksumAlgorithm; - @Column(name = FIRMWARE_CHECKSUM_COLUMN) + @Column(name = OTA_PACKAGE_CHECKSUM_COLUMN) private String checksum; - @Column(name = FIRMWARE_DATA_SIZE_COLUMN) + @Column(name = OTA_PACKAGE_DATA_SIZE_COLUMN) private Long dataSize; @Type(type = "json") - @Column(name = ModelConstants.FIRMWARE_ADDITIONAL_INFO_COLUMN) + @Column(name = ModelConstants.OTA_PACKAGE_ADDITIONAL_INFO_COLUMN) private JsonNode additionalInfo; @Column(name = SEARCH_TEXT_PROPERTY) @@ -102,11 +102,11 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S @Transient private boolean hasData; - public FirmwareInfoEntity() { + public OtaPackageInfoEntity() { super(); } - public FirmwareInfoEntity(FirmwareInfo firmware) { + public OtaPackageInfoEntity(OtaPackageInfo firmware) { this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); @@ -124,9 +124,9 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S this.additionalInfo = firmware.getAdditionalInfo(); } - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version, - String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, - Object additionalInfo, boolean hasData) { + public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, + String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, + Object additionalInfo, boolean hasData) { this.id = id; this.createdTime = createdTime; this.tenantId = tenantId; @@ -154,8 +154,8 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S } @Override - public FirmwareInfo toData() { - FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); + public OtaPackageInfo toData() { + OtaPackageInfo firmware = new OtaPackageInfo(new OtaPackageId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); if (deviceProfileId != null) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java new file mode 100644 index 0000000000..ae19576861 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -0,0 +1,373 @@ +/** + * Copyright © 2016-2021 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.dao.ota; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.thingsboard.server.cache.ota.OtaPackageDataCache; +import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.device.DeviceProfileDao; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TenantDao; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validatePageLink; + +@Service +@Slf4j +@RequiredArgsConstructor +public class BaseOtaPackageService implements OtaPackageService { + public static final String INCORRECT_OTA_PACKAGE_ID = "Incorrect otaPackageId "; + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + + private final TenantDao tenantDao; + private final DeviceProfileDao deviceProfileDao; + private final OtaPackageDao otaPackageDao; + private final OtaPackageInfoDao otaPackageInfoDao; + private final CacheManager cacheManager; + private final OtaPackageDataCache otaPackageDataCache; + + @Override + public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { + log.trace("Executing saveOtaPackageInfo [{}]", otaPackageInfo); + otaPackageInfoValidator.validate(otaPackageInfo, OtaPackageInfo::getTenantId); + try { + OtaPackageId otaPackageId = otaPackageInfo.getId(); + if (otaPackageId != null) { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + } + return otaPackageInfoDao.save(otaPackageInfo.getTenantId(), otaPackageInfo); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("ota_package_tenant_title_version_unq_key")) { + throw new DataValidationException("OtaPackage with such title and version already exists!"); + } else { + throw t; + } + } + } + + @Override + public OtaPackage saveOtaPackage(OtaPackage otaPackage) { + log.trace("Executing saveOtaPackage [{}]", otaPackage); + otaPackageValidator.validate(otaPackage, OtaPackageInfo::getTenantId); + try { + OtaPackageId otaPackageId = otaPackage.getId(); + if (otaPackageId != null) { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + } + return otaPackageDao.save(otaPackage.getTenantId(), otaPackage); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("ota_package_tenant_title_version_unq_key")) { + throw new DataValidationException("OtaPackage with such title and version already exists!"); + } else { + throw t; + } + } + } + + @Override + public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) { + if (data == null || !data.hasArray() || data.array().length == 0) { + throw new DataValidationException("OtaPackage data should be specified!"); + } + + return getHashFunction(checksumAlgorithm).hashBytes(data.array()).toString(); + } + + private HashFunction getHashFunction(ChecksumAlgorithm checksumAlgorithm) { + switch (checksumAlgorithm) { + case MD5: + return Hashing.md5(); + case SHA256: + return Hashing.sha256(); + case SHA384: + return Hashing.sha384(); + case SHA512: + return Hashing.sha512(); + case CRC32: + return Hashing.crc32(); + case MURMUR3_32: + return Hashing.murmur3_32(); + case MURMUR3_128: + return Hashing.murmur3_128(); + default: + throw new DataValidationException("Unknown checksum algorithm!"); + } + } + + @Override + public OtaPackage findOtaPackageById(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageById [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageDao.findById(tenantId, otaPackageId.getId()); + } + + @Override + @Cacheable(cacheNames = OTA_PACKAGE_CACHE, key = "{#otaPackageId}") + public OtaPackageInfo findOtaPackageInfoById(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageInfoById [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageInfoDao.findById(tenantId, otaPackageId.getId()); + } + + @Override + public ListenableFuture findOtaPackageInfoByIdAsync(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing findOtaPackageInfoByIdAsync [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + return otaPackageInfoDao.findByIdAsync(tenantId, otaPackageId.getId()); + } + + @Override + public PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink) { + log.trace("Executing findTenantOtaPackagesByTenantId, tenantId [{}], pageLink [{}]", tenantId, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return otaPackageInfoDao.findOtaPackageInfoByTenantId(tenantId, pageLink); + } + + @Override + public PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { + log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validatePageLink(pageLink); + return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, hasData, pageLink); + } + + @Override + public void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId) { + log.trace("Executing deleteOtaPackage [{}]", otaPackageId); + validateId(otaPackageId, INCORRECT_OTA_PACKAGE_ID + otaPackageId); + try { + Cache cache = cacheManager.getCache(OTA_PACKAGE_CACHE); + cache.evict(toOtaPackageInfoKey(otaPackageId)); + otaPackageDataCache.evict(otaPackageId.toString()); + otaPackageDao.removeById(tenantId, otaPackageId.getId()); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device")) { + throw new DataValidationException("The otaPackage referenced by the devices cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { + throw new DataValidationException("The otaPackage referenced by the device profile cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { + throw new DataValidationException("The software referenced by the devices cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { + throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); + } else { + throw t; + } + } + } + + @Override + public void deleteOtaPackagesByTenantId(TenantId tenantId) { + log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + tenantOtaPackageRemover.removeEntities(tenantId, tenantId); + } + + private DataValidator otaPackageInfoValidator = new DataValidator<>() { + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackageInfo otaPackageInfo) { + validateImpl(otaPackageInfo); + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { + OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); + + validateUpdateDeviceProfile(otaPackage, otaPackageOld); + BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); + } + }; + + private DataValidator otaPackageValidator = new DataValidator<>() { + + @Override + protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { + validateImpl(otaPackage); + + if (StringUtils.isEmpty(otaPackage.getFileName())) { + throw new DataValidationException("OtaPackage file name should be specified!"); + } + + if (StringUtils.isEmpty(otaPackage.getContentType())) { + throw new DataValidationException("OtaPackage content type should be specified!"); + } + + if (otaPackage.getChecksumAlgorithm() == null) { + throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); + } + if (StringUtils.isEmpty(otaPackage.getChecksum())) { + throw new DataValidationException("OtaPackage checksum should be specified!"); + } + + String currentChecksum; + + currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); + + if (!currentChecksum.equals(otaPackage.getChecksum())) { + throw new DataValidationException("Wrong otaPackage file!"); + } + } + + @Override + protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { + OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); + + validateUpdateDeviceProfile(otaPackage, otaPackageOld); + BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); + + if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { + throw new DataValidationException("Updating otaPackage data is prohibited!"); + } + } + }; + + private void validateUpdateDeviceProfile(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { + if (otaPackageOld.getDeviceProfileId() != null && !otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { + if (otaPackageInfoDao.isOtaPackageUsed(otaPackageOld.getId(), otaPackage.getType(), otaPackageOld.getDeviceProfileId())) { + throw new DataValidationException("Can`t update deviceProfileId because otaPackage is already in use!"); + } + } + } + + private static void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { + if (!otaPackageOld.getType().equals(otaPackage.getType())) { + throw new DataValidationException("Updating type is prohibited!"); + } + + if (!otaPackageOld.getTitle().equals(otaPackage.getTitle())) { + throw new DataValidationException("Updating otaPackage title is prohibited!"); + } + + if (!otaPackageOld.getVersion().equals(otaPackage.getVersion())) { + throw new DataValidationException("Updating otaPackage version is prohibited!"); + } + + if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { + throw new DataValidationException("Updating otaPackage file name is prohibited!"); + } + + if (otaPackageOld.getContentType() != null && !otaPackageOld.getContentType().equals(otaPackage.getContentType())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksumAlgorithm() != null && !otaPackageOld.getChecksumAlgorithm().equals(otaPackage.getChecksumAlgorithm())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getChecksum() != null && !otaPackageOld.getChecksum().equals(otaPackage.getChecksum())) { + throw new DataValidationException("Updating otaPackage content type is prohibited!"); + } + + if (otaPackageOld.getDataSize() != null && !otaPackageOld.getDataSize().equals(otaPackage.getDataSize())) { + throw new DataValidationException("Updating otaPackage data size is prohibited!"); + } + } + + private void validateImpl(OtaPackageInfo otaPackageInfo) { + if (otaPackageInfo.getTenantId() == null) { + throw new DataValidationException("OtaPackage should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent tenant!"); + } + } + + if (otaPackageInfo.getDeviceProfileId() != null) { + DeviceProfile deviceProfile = deviceProfileDao.findById(otaPackageInfo.getTenantId(), otaPackageInfo.getDeviceProfileId().getId()); + if (deviceProfile == null) { + throw new DataValidationException("OtaPackage is referencing to non-existent device profile!"); + } + } + + if (otaPackageInfo.getType() == null) { + throw new DataValidationException("Type should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getTitle())) { + throw new DataValidationException("OtaPackage title should be specified!"); + } + + if (StringUtils.isEmpty(otaPackageInfo.getVersion())) { + throw new DataValidationException("OtaPackage version should be specified!"); + } + } + + private PaginatedRemover tenantOtaPackageRemover = + new PaginatedRemover<>() { + + @Override + protected PageData findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { + return otaPackageInfoDao.findOtaPackageInfoByTenantId(id, pageLink); + } + + @Override + protected void removeEntity(TenantId tenantId, OtaPackageInfo entity) { + deleteOtaPackage(tenantId, entity.getId()); + } + }; + + protected Optional extractConstraintViolationException(Exception t) { + if (t instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) t); + } else if (t.getCause() instanceof ConstraintViolationException) { + return Optional.of((ConstraintViolationException) (t.getCause())); + } else { + return Optional.empty(); + } + } + + private static List toOtaPackageInfoKey(OtaPackageId otaPackageId) { + return Collections.singletonList(otaPackageId); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java rename to dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index 0cacb47ea0..42f66663d1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.firmware; +package org.thingsboard.server.dao.ota; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.dao.Dao; -public interface FirmwareDao extends Dao { +public interface OtaPackageDao extends Dao { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java similarity index 55% rename from dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java index 7cb6c3a57f..d3294f0ec3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java @@ -13,25 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.firmware; +package org.thingsboard.server.dao.ota; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; -import java.util.UUID; +public interface OtaPackageInfoDao extends Dao { -public interface FirmwareInfoDao extends Dao { + PageData findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink); - PageData findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); + PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); - PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); - - boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId); + boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index 3bbe18f2ab..58c05547b0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -96,23 +96,35 @@ public interface DeviceRepository extends PagingAndSortingRepository findByTenantIdAndTypeAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, - @Param("type") String type, + @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, Pageable pageable); @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + - "AND d.type = :type " + + "AND d.deviceProfileId = :deviceProfileId " + "AND d.softwareId = null " + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") Page findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, - @Param("type") String type, + @Param("deviceProfileId") UUID deviceProfileId, @Param("textSearch") String textSearch, Pageable pageable); + @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + + "AND d.deviceProfileId = :deviceProfileId " + + "AND d.firmwareId = null") + Long countByTenantIdAndDeviceProfileIdAndFirmwareIdIsNull(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId); + + @Query("SELECT count(*) FROM DeviceEntity d WHERE d.tenantId = :tenantId " + + "AND d.deviceProfileId = :deviceProfileId " + + "AND d.softwareId = null") + Long countByTenantIdAndDeviceProfileIdAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId); + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + "LEFT JOIN CustomerEntity c on c.id = d.customerId " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 344286d7ac..0e3e843405 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.sql.device; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; @@ -27,6 +29,8 @@ import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.ota.OtaPackageUtil; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; @@ -155,23 +159,27 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull( - tenantId, - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public PageData findDevicesByTenantIdAndTypeAndEmptyOtaPackage(UUID tenantId, + UUID deviceProfileId, + OtaPackageType type, + PageLink pageLink) { + Pageable pageable = DaoUtil.toPageable(pageLink); + String searchText = Objects.toString(pageLink.getTextSearch(), ""); + Page page = OtaPackageUtil.getByOtaPackageType( + () -> deviceRepository.findByTenantIdAndTypeAndFirmwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), + () -> deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(tenantId, deviceProfileId, searchText, pageable), + type + ); + return DaoUtil.toPageData(page); } @Override - public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) { - return DaoUtil.toPageData( - deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull( - tenantId, - type, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(UUID tenantId, UUID deviceProfileId, OtaPackageType type) { + return OtaPackageUtil.getByOtaPackageType( + () -> deviceRepository.countByTenantIdAndDeviceProfileIdAndFirmwareIdIsNull(tenantId, deviceProfileId), + () -> deviceRepository.countByTenantIdAndDeviceProfileIdAndSoftwareIdIsNull(tenantId, deviceProfileId), + type + ); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java deleted file mode 100644 index dab6ce3304..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.sql.firmware; - -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.CrudRepository; -import org.springframework.data.repository.query.Param; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; - -import java.util.UUID; - -public interface FirmwareInfoRepository extends CrudRepository { - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + - "f.tenantId = :tenantId " + - "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findAllByTenantId(@Param("tenantId") UUID tenantId, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + - "f.tenantId = :tenantId " + - "AND f.deviceProfileId = :deviceProfileId " + - "AND f.type = :type " + - "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + - "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, - @Param("deviceProfileId") UUID deviceProfileId, - @Param("type") FirmwareType type, - @Param("hasData") boolean hasData, - @Param("searchText") String searchText, - Pageable pageable); - - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") - FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); - - @Query(value = "SELECT exists(SELECT * " + - "FROM device_profile AS dp " + - "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + - "WHERE dp.id = :deviceProfileId AND " + - "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " + - "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true) - boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java deleted file mode 100644 index 8cae33a057..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * Copyright © 2016-2021 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.dao.sql.firmware; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.repository.CrudRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.FirmwareInfo; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.FirmwareId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.firmware.FirmwareInfoDao; -import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; -import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; - -import java.util.Objects; -import java.util.UUID; - -@Slf4j -@Component -public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao implements FirmwareInfoDao { - - @Autowired - private FirmwareInfoRepository firmwareInfoRepository; - - @Override - protected Class getEntityClass() { - return FirmwareInfoEntity.class; - } - - @Override - protected CrudRepository getCrudRepository() { - return firmwareInfoRepository; - } - - @Override - public FirmwareInfo findById(TenantId tenantId, UUID id) { - return DaoUtil.getData(firmwareInfoRepository.findFirmwareInfoById(id)); - } - - @Override - public FirmwareInfo save(TenantId tenantId, FirmwareInfo firmwareInfo) { - FirmwareInfo savedFirmware = super.save(tenantId, firmwareInfo); - if (firmwareInfo.getId() == null) { - return savedFirmware; - } else { - return findById(tenantId, savedFirmware.getId().getId()); - } - } - - @Override - public PageData findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink) { - return DaoUtil.toPageData(firmwareInfoRepository - .findAllByTenantId( - tenantId.getId(), - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); - } - - @Override - public PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { - return DaoUtil.toPageData(firmwareInfoRepository - .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( - tenantId.getId(), - deviceProfileId.getId(), - firmwareType, - hasData, - Objects.toString(pageLink.getTextSearch(), ""), - DaoUtil.toPageable(pageLink))); - } - - @Override - public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) { - return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name()); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java similarity index 62% rename from dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java index 44b956c298..95737ca48d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java @@ -13,34 +13,34 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.firmware; +package org.thingsboard.server.dao.sql.ota; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.dao.firmware.FirmwareDao; -import org.thingsboard.server.dao.model.sql.FirmwareEntity; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.dao.ota.OtaPackageDao; +import org.thingsboard.server.dao.model.sql.OtaPackageEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; import java.util.UUID; @Slf4j @Component -public class JpaFirmwareDao extends JpaAbstractSearchTextDao implements FirmwareDao { +public class JpaOtaPackageDao extends JpaAbstractSearchTextDao implements OtaPackageDao { @Autowired - private FirmwareRepository firmwareRepository; + private OtaPackageRepository otaPackageRepository; @Override - protected Class getEntityClass() { - return FirmwareEntity.class; + protected Class getEntityClass() { + return OtaPackageEntity.class; } @Override - protected CrudRepository getCrudRepository() { - return firmwareRepository; + protected CrudRepository getCrudRepository() { + return otaPackageRepository; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java new file mode 100644 index 0000000000..0d35c8ee0d --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageInfoDao.java @@ -0,0 +1,94 @@ +/** + * Copyright © 2016-2021 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.dao.sql.ota; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.OtaPackageId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.ota.OtaPackageInfoDao; +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; +import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; + +import java.util.Objects; +import java.util.UUID; + +@Slf4j +@Component +public class JpaOtaPackageInfoDao extends JpaAbstractSearchTextDao implements OtaPackageInfoDao { + + @Autowired + private OtaPackageInfoRepository otaPackageInfoRepository; + + @Override + protected Class getEntityClass() { + return OtaPackageInfoEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return otaPackageInfoRepository; + } + + @Override + public OtaPackageInfo findById(TenantId tenantId, UUID id) { + return DaoUtil.getData(otaPackageInfoRepository.findOtaPackageInfoById(id)); + } + + @Override + public OtaPackageInfo save(TenantId tenantId, OtaPackageInfo otaPackageInfo) { + OtaPackageInfo savedOtaPackage = super.save(tenantId, otaPackageInfo); + if (otaPackageInfo.getId() == null) { + return savedOtaPackage; + } else { + return findById(tenantId, savedOtaPackage.getId().getId()); + } + } + + @Override + public PageData findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink) { + return DaoUtil.toPageData(otaPackageInfoRepository + .findAllByTenantId( + tenantId.getId(), + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { + return DaoUtil.toPageData(otaPackageInfoRepository + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( + tenantId.getId(), + deviceProfileId.getId(), + otaPackageType, + hasData, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + + @Override + public boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { + return otaPackageInfoRepository.isOtaPackageUsed(otaPackageId.getId(), deviceProfileId.getId(), otaPackageType.name()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java new file mode 100644 index 0000000000..9848f83200 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java @@ -0,0 +1,60 @@ +/** + * Copyright © 2016-2021 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.dao.sql.ota; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.ota.OtaPackageType; +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; + +import java.util.UUID; + +public interface OtaPackageInfoRepository extends CrudRepository { + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + "f.tenantId = :tenantId " + + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") + Page findAllByTenantId(@Param("tenantId") UUID tenantId, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + "f.tenantId = :tenantId " + + "AND f.deviceProfileId = :deviceProfileId " + + "AND f.type = :type " + + "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") + Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId, + @Param("type") OtaPackageType type, + @Param("hasData") boolean hasData, + @Param("searchText") String searchText, + Pageable pageable); + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE f.id = :id") + OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id); + + @Query(value = "SELECT exists(SELECT * " + + "FROM device_profile AS dp " + + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + + "WHERE dp.id = :deviceProfileId AND " + + "(('FIRMWARE' = :type AND (dp.firmware_id = :otaPackageId OR d.firmware_id = :otaPackageId)) " + + "OR ('SOFTWARE' = :type AND (dp.software_id = :otaPackageId or d.software_id = :otaPackageId))))", nativeQuery = true) + boolean isOtaPackageUsed(@Param("otaPackageId") UUID otaPackageId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java similarity index 78% rename from dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java index a969507798..3699005ff2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.firmware; +package org.thingsboard.server.dao.sql.ota; import org.springframework.data.repository.CrudRepository; -import org.thingsboard.server.dao.model.sql.FirmwareEntity; +import org.thingsboard.server.dao.model.sql.OtaPackageEntity; import java.util.UUID; -public interface FirmwareRepository extends CrudRepository { +public interface OtaPackageRepository extends CrudRepository { } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 6391ed73e7..c5050467dd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -35,7 +35,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; @@ -94,7 +94,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe private ResourceService resourceService; @Autowired - private FirmwareService firmwareService; + private OtaPackageService otaPackageService; @Override public Tenant findTenantById(TenantId tenantId) { @@ -150,7 +150,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe ruleChainService.deleteRuleChainsByTenantId(tenantId); apiUsageStateService.deleteApiUsageStateByTenantId(tenantId); resourceService.deleteResourcesByTenantId(tenantId); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); tenantDao.removeById(tenantId, tenantId.getId()); deleteEntityRelations(tenantId, tenantId); } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index fcb7f82ed0..dfc1821f66 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -160,8 +160,8 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid , @@ -176,7 +176,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); CREATE TABLE IF NOT EXISTS device_profile ( @@ -202,8 +202,8 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device ( @@ -222,8 +222,8 @@ CREATE TABLE IF NOT EXISTS device ( software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 30b9a3dbe7..be7e836a65 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -178,8 +178,8 @@ CREATE TABLE IF NOT EXISTS rule_node_state ( CONSTRAINT fk_rule_node_state_node_id FOREIGN KEY (rule_node_id) REFERENCES rule_node(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS firmware ( - id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS ota_package ( + id uuid NOT NULL CONSTRAINT ota_package_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, device_profile_id uuid , @@ -194,7 +194,7 @@ CREATE TABLE IF NOT EXISTS firmware ( data_size bigint, additional_info varchar, search_text varchar(255), - CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) + CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) -- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE ); @@ -221,8 +221,8 @@ CREATE TABLE IF NOT EXISTS device_profile ( CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), CONSTRAINT fk_default_dashboard_device_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES ota_package(id) ); -- We will use one-to-many relation in the first release and extend this feature in case of user requests @@ -250,8 +250,8 @@ CREATE TABLE IF NOT EXISTS device ( software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), - CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES ota_package(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES ota_package(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 091ab1741e..2aeb44c0be 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -54,7 +54,7 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; -import org.thingsboard.server.dao.firmware.FirmwareService; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; @@ -160,7 +160,7 @@ public abstract class AbstractServiceTest { @Autowired - protected FirmwareService firmwareService; + protected OtaPackageService otaPackageService; public class IdComparator implements Comparator { @Override diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index 5516cb6181..e53b740cbf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -28,10 +28,9 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.firmware.FirmwareType; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -45,7 +44,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.stream.Collectors; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; public class BaseDeviceProfileServiceTest extends AbstractServiceTest { @@ -99,7 +98,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { Assert.assertEquals(deviceProfile.isDefault(), savedDeviceProfile.isDefault()); Assert.assertEquals(deviceProfile.getDefaultRuleChainId(), savedDeviceProfile.getDefaultRuleChainId()); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -110,7 +109,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); deviceProfile.setFirmwareId(savedFirmware.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index 587fce1ed5..f335ffacd3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -28,10 +28,10 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; -import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantProfile; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -46,7 +46,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -189,7 +189,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { Assert.assertEquals(20, deviceCredentials.getCredentialsId().length()); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(device.getDeviceProfileId()); firmware.setType(FIRMWARE); @@ -200,7 +200,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); @@ -223,7 +223,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile); Assert.assertNotNull(savedProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedProfile.getId()); firmware.setType(FIRMWARE); @@ -234,7 +234,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java similarity index 74% rename from dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index bd9e0ed372..e7cee8c878 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -25,10 +25,10 @@ import org.junit.rules.ExpectedException; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.Firmware; -import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -40,9 +40,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; -public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { +public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { public static final String TITLE = "My firmware"; private static final String FILE_NAME = "filename.txt"; @@ -52,7 +52,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); - private IdComparator idComparator = new IdComparator<>(); + private IdComparator idComparator = new IdComparator<>(); private TenantId tenantId; @@ -82,7 +82,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { @Test public void testSaveFirmware() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -93,7 +93,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Assert.assertNotNull(savedFirmware); Assert.assertNotNull(savedFirmware.getId()); @@ -105,23 +105,23 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmware.getData(), savedFirmware.getData()); savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode()); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testSaveFirmwareInfoAndUpdateWithData() { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); + OtaPackageInfo savedFirmwareInfo = otaPackageService.saveOtaPackageInfo(firmwareInfo); Assert.assertNotNull(savedFirmwareInfo); Assert.assertNotNull(savedFirmwareInfo.getId()); @@ -129,7 +129,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwareInfo.getTenantId(), savedFirmwareInfo.getTenantId()); Assert.assertEquals(firmwareInfo.getTitle(), savedFirmwareInfo.getTitle()); - Firmware firmware = new Firmware(savedFirmwareInfo.getId()); + OtaPackage firmware = new OtaPackage(savedFirmwareInfo.getId()); firmware.setCreatedTime(firmwareInfo.getCreatedTime()); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); @@ -142,24 +142,24 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); - savedFirmwareInfo = firmwareService.findFirmwareInfoById(tenantId, savedFirmwareInfo.getId()); + savedFirmwareInfo = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmwareInfo.getId()); savedFirmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode()); - firmwareService.saveFirmwareInfo(savedFirmwareInfo); + otaPackageService.saveOtaPackageInfo(savedFirmwareInfo); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, firmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, firmware.getId()); firmware.setAdditionalInfo(JacksonUtil.newObjectNode()); Assert.assertEquals(foundFirmware.getTitle(), firmware.getTitle()); Assert.assertTrue(foundFirmware.isHasData()); - firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmwareInfo.getId()); } @Test public void testSaveFirmwareWithEmptyTenant() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); firmware.setTitle(TITLE); @@ -171,13 +171,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware should be assigned to tenant!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage should be assigned to tenant!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyType() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setTitle(TITLE); @@ -190,12 +190,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { thrown.expect(DataValidationException.class); thrown.expectMessage("Type should be specified!"); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyTitle() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -207,13 +207,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware title should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage title should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyFileName() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -225,13 +225,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware file name should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage file name should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyContentType() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -243,13 +243,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware content type should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage content type should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyData() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -261,13 +261,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware data should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage data should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithInvalidTenant() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(new TenantId(Uuids.timeBased())); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -280,13 +280,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware is referencing to non-existent tenant!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage is referencing to non-existent tenant!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithInvalidDeviceProfileId() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased())); firmware.setType(FIRMWARE); @@ -299,13 +299,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware is referencing to non-existent device profile!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage is referencing to non-existent device profile!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareWithEmptyChecksum() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -317,21 +317,21 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware checksum should be specified!"); - firmwareService.saveFirmware(firmware); + thrown.expectMessage("OtaPackage checksum should be specified!"); + otaPackageService.saveOtaPackage(firmware); } @Test public void testSaveFirmwareInfoWithExistingTitleAndVersion() { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); - firmwareService.saveFirmwareInfo(firmwareInfo); + otaPackageService.saveOtaPackageInfo(firmwareInfo); - FirmwareInfo newFirmwareInfo = new FirmwareInfo(); + OtaPackageInfo newFirmwareInfo = new OtaPackageInfo(); newFirmwareInfo.setTenantId(tenantId); newFirmwareInfo.setDeviceProfileId(deviceProfileId); newFirmwareInfo.setType(FIRMWARE); @@ -339,13 +339,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { newFirmwareInfo.setVersion(VERSION); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware with such title and version already exists!"); - firmwareService.saveFirmwareInfo(newFirmwareInfo); + thrown.expectMessage("OtaPackage with such title and version already exists!"); + otaPackageService.saveOtaPackageInfo(newFirmwareInfo); } @Test public void testSaveFirmwareWithExistingTitleAndVersion() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -356,9 +356,9 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); - Firmware newFirmware = new Firmware(); + OtaPackage newFirmware = new OtaPackage(); newFirmware.setTenantId(tenantId); newFirmware.setDeviceProfileId(deviceProfileId); newFirmware.setType(FIRMWARE); @@ -371,13 +371,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { newFirmware.setData(DATA); thrown.expect(DataValidationException.class); - thrown.expectMessage("Firmware with such title and version already exists!"); - firmwareService.saveFirmware(newFirmware); + thrown.expectMessage("OtaPackage with such title and version already exists!"); + otaPackageService.saveOtaPackage(newFirmware); } @Test public void testDeleteFirmwareWithReferenceByDevice() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -388,7 +388,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Device device = new Device(); device.setTenantId(tenantId); @@ -399,17 +399,17 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { try { thrown.expect(DataValidationException.class); - thrown.expectMessage("The firmware referenced by the devices cannot be deleted!"); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + thrown.expectMessage("The otaPackage referenced by the devices cannot be deleted!"); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } finally { deviceService.deleteDevice(tenantId, savedDevice.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @Test public void testUpdateDeviceProfileIdWithReferenceByDevice() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -420,7 +420,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Device device = new Device(); device.setTenantId(tenantId); @@ -431,12 +431,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); savedFirmware.setDeviceProfileId(null); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); } finally { deviceService.deleteDevice(tenantId, savedDevice.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -445,7 +445,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -456,18 +456,18 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); deviceProfileService.saveDeviceProfile(savedDeviceProfile); try { thrown.expect(DataValidationException.class); - thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!"); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + thrown.expectMessage("The otaPackage referenced by the device profile cannot be deleted!"); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } finally { deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -476,7 +476,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(savedDeviceProfile.getId()); firmware.setType(FIRMWARE); @@ -487,25 +487,25 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); deviceProfileService.saveDeviceProfile(savedDeviceProfile); try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); savedFirmware.setDeviceProfileId(null); - firmwareService.saveFirmware(savedFirmware); + otaPackageService.saveOtaPackage(savedFirmware); } finally { deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @Test public void testFindFirmwareById() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -516,33 +516,33 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testFindFirmwareInfoById() { - FirmwareInfo firmware = new FirmwareInfo(); + OtaPackageInfo firmware = new OtaPackageInfo(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); - FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); + OtaPackageInfo savedFirmware = otaPackageService.saveOtaPackageInfo(firmware); - FirmwareInfo foundFirmware = firmwareService.findFirmwareInfoById(tenantId, savedFirmware.getId()); + OtaPackageInfo foundFirmware = otaPackageService.findOtaPackageInfoById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); Assert.assertEquals(savedFirmware, foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } @Test public void testDeleteFirmware() { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -553,20 +553,20 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - Firmware savedFirmware = firmwareService.saveFirmware(firmware); + OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Firmware foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); - firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); - foundFirmware = firmwareService.findFirmwareById(tenantId, savedFirmware.getId()); + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); + foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNull(foundFirmware); } @Test public void testFindTenantFirmwaresByTenantId() { - List firmwares = new ArrayList<>(); + List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - Firmware firmware = new Firmware(); + OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); firmware.setType(FIRMWARE); @@ -578,16 +578,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); - FirmwareInfo info = new FirmwareInfo(firmwareService.saveFirmware(firmware)); + OtaPackageInfo info = new OtaPackageInfo(otaPackageService.saveOtaPackage(firmware)); info.setHasData(true); firmwares.add(info); } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); - PageData pageData; + PageData pageData; do { - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -599,19 +599,19 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); pageLink = new PageLink(31); - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); } @Test public void testFindTenantFirmwaresByTenantIdAndHasData() { - List firmwares = new ArrayList<>(); + List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - FirmwareInfo firmwareInfo = new FirmwareInfo(); + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); firmwareInfo.setTenantId(tenantId); firmwareInfo.setDeviceProfileId(deviceProfileId); firmwareInfo.setType(FIRMWARE); @@ -622,14 +622,14 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmwareInfo.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmwareInfo.setChecksum(CHECKSUM); firmwareInfo.setDataSize((long) DATA.array().length); - firmwares.add(firmwareService.saveFirmwareInfo(firmwareInfo)); + firmwares.add(otaPackageService.saveOtaPackageInfo(firmwareInfo)); } - List loadedFirmwares = new ArrayList<>(); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); - PageData pageData; + PageData pageData; do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -642,7 +642,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); firmwares.forEach(f -> { - Firmware firmware = new Firmware(f.getId()); + OtaPackage firmware = new OtaPackage(f.getId()); firmware.setCreatedTime(f.getCreatedTime()); firmware.setTenantId(f.getTenantId()); firmware.setDeviceProfileId(deviceProfileId); @@ -655,14 +655,14 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksum(CHECKSUM); firmware.setData(DATA); firmware.setDataSize((long) DATA.array().length); - firmwareService.saveFirmware(firmware); + otaPackageService.saveOtaPackage(firmware); f.setHasData(true); }); loadedFirmwares = new ArrayList<>(); pageLink = new PageLink(16); do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -674,10 +674,10 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Assert.assertEquals(firmwares, loadedFirmwares); - firmwareService.deleteFirmwaresByTenantId(tenantId); + otaPackageService.deleteOtaPackagesByTenantId(tenantId); pageLink = new PageLink(31); - pageData = firmwareService.findTenantFirmwaresByTenantId(tenantId, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertTrue(pageData.getData().isEmpty()); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java similarity index 83% rename from dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java rename to dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java index a89414e3a2..59ac4f76bf 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/sql/FirmwareServiceSqlTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OtaPackageServiceSqlTest.java @@ -15,9 +15,9 @@ */ package org.thingsboard.server.dao.service.sql; -import org.thingsboard.server.dao.service.BaseFirmwareServiceTest; +import org.thingsboard.server.dao.service.BaseOtaPackageServiceTest; import org.thingsboard.server.dao.service.DaoSqlTest; @DaoSqlTest -public class FirmwareServiceSqlTest extends BaseFirmwareServiceTest { +public class OtaPackageServiceSqlTest extends BaseOtaPackageServiceTest { } diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 2805c78fdd..74eb4f43f0 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -36,8 +36,8 @@ caffeine.specs.tenantProfiles.maxSize=100000 caffeine.specs.deviceProfiles.timeToLiveInMinutes=1440 caffeine.specs.deviceProfiles.maxSize=100000 -caffeine.specs.firmwares.timeToLiveInMinutes=1440 -caffeine.specs.firmwares.maxSize=100000 +caffeine.specs.otaPackages.timeToLiveInMinutes=1440 +caffeine.specs.otaPackages.maxSize=100000 caffeine.specs.edges.timeToLiveInMinutes=1440 caffeine.specs.edges.maxSize=100000 diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index f65316f2c1..726b4ba412 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -29,7 +29,7 @@ DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; -DROP TABLE IF EXISTS firmware; +DROP TABLE IF EXISTS ota_package; DROP TABLE IF EXISTS edge; DROP TABLE IF EXISTS edge_event; DROP FUNCTION IF EXISTS to_uuid; From 9d93f429e81bfc57537cb6c3f80cde2c40616280 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 31 May 2021 18:15:31 +0300 Subject: [PATCH 20/75] UI: Rename firmware to OtaPackage --- ui-ngx/src/app/core/http/entity.service.ts | 20 +-- ui-ngx/src/app/core/http/firmware.service.ts | 123 ------------------ .../src/app/core/http/ota-package.service.ts | 123 ++++++++++++++++++ ui-ngx/src/app/core/services/menu.service.ts | 8 +- ...device-profile-autocomplete.component.html | 1 + .../device-profile-autocomplete.component.ts | 3 + .../profile/device-profile.component.html | 12 +- .../profile/device-profile.component.ts | 4 +- .../app/modules/home/models/services.map.ts | 4 +- .../home/pages/device/device.component.html | 12 +- .../home/pages/device/device.component.ts | 4 +- .../firmware/firmware-table-config.resolve.ts | 115 ---------------- .../modules/home/pages/home-pages.module.ts | 4 +- .../ota-update-routing.module.ts} | 14 +- .../ota-update-table-config.resolve.ts | 116 +++++++++++++++++ .../ota-update.component.html} | 80 ++++++------ .../ota-update.component.ts} | 44 ++++--- .../ota-update.module.ts} | 10 +- .../ota-package-autocomplete.component.html} | 27 ++-- .../ota-package-autocomplete.component.ts} | 110 +++++++--------- ui-ngx/src/app/shared/models/constants.ts | 1 + ui-ngx/src/app/shared/models/device.models.ts | 10 +- .../app/shared/models/entity-type.models.ts | 20 +-- .../id/{firmware-id.ts => ota-package-id.ts} | 4 +- ...rmware.models.ts => ota-package.models.ts} | 43 ++++-- ui-ngx/src/app/shared/shared.module.ts | 6 +- .../assets/locale/locale.constant-en_US.json | 96 +++++++------- 27 files changed, 521 insertions(+), 493 deletions(-) delete mode 100644 ui-ngx/src/app/core/http/firmware.service.ts create mode 100644 ui-ngx/src/app/core/http/ota-package.service.ts delete mode 100644 ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts rename ui-ngx/src/app/modules/home/pages/{firmware/firmware-routing.module.ts => ota-update/ota-update-routing.module.ts} (78%) create mode 100644 ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts rename ui-ngx/src/app/modules/home/pages/{firmware/firmwares.component.html => ota-update/ota-update.component.html} (60%) rename ui-ngx/src/app/modules/home/pages/{firmware/firmwares.component.ts => ota-update/ota-update.component.ts} (74%) rename ui-ngx/src/app/modules/home/pages/{firmware/firmware.module.ts => ota-update/ota-update.module.ts} (79%) rename ui-ngx/src/app/shared/components/{firmware/firmware-autocomplete.component.html => ota-package/ota-package-autocomplete.component.html} (62%) rename ui-ngx/src/app/shared/components/{firmware/firmware-autocomplete.component.ts => ota-package/ota-package-autocomplete.component.ts} (59%) rename ui-ngx/src/app/shared/models/id/{firmware-id.ts => ota-package-id.ts} (90%) rename ui-ngx/src/app/shared/models/{firmware.models.ts => ota-package.models.ts} (58%) diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index c5ef4b8db4..c58e5ac909 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -75,12 +75,12 @@ import { StringOperation } from '@shared/models/query/query.models'; import { alarmFields } from '@shared/models/alarm.models'; -import { FirmwareService } from '@core/http/firmware.service'; -import { EdgeService } from "@core/http/edge.service"; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { EdgeService } from '@core/http/edge.service'; import { Edge, EdgeEventType } from '@shared/models/edge.models'; -import { RuleChainType } from "@shared/models/rule-chain.models"; -import { WidgetService } from "@core/http/widget.service"; -import { DeviceProfileService } from "@core/http/device-profile.service"; +import { RuleChainType } from '@shared/models/rule-chain.models'; +import { WidgetService } from '@core/http/widget.service'; +import { DeviceProfileService } from '@core/http/device-profile.service'; @Injectable({ providedIn: 'root' @@ -101,7 +101,7 @@ export class EntityService { private dashboardService: DashboardService, private entityRelationService: EntityRelationService, private attributeService: AttributeService, - private firmwareService: FirmwareService, + private otaPackageService: OtaPackageService, private widgetService: WidgetService, private deviceProfileService: DeviceProfileService, private utils: UtilsService @@ -142,8 +142,8 @@ export class EntityService { case EntityType.ALARM: console.error('Get Alarm Entity is not implemented!'); break; - case EntityType.FIRMWARE: - observable = this.firmwareService.getFirmwareInfo(entityId, config); + case EntityType.OTA_PACKAGE: + observable = this.otaPackageService.getOtaPackageInfo(entityId, config); break; } return observable; @@ -359,9 +359,9 @@ export class EntityService { case EntityType.ALARM: console.error('Get Alarm Entities is not implemented!'); break; - case EntityType.FIRMWARE: + case EntityType.OTA_PACKAGE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.firmwareService.getFirmwares(pageLink, config); + entitiesObservable = this.otaPackageService.getOtaPackages(pageLink, config); break; } return entitiesObservable; diff --git a/ui-ngx/src/app/core/http/firmware.service.ts b/ui-ngx/src/app/core/http/firmware.service.ts deleted file mode 100644 index 1fe51c5f68..0000000000 --- a/ui-ngx/src/app/core/http/firmware.service.ts +++ /dev/null @@ -1,123 +0,0 @@ -/// -/// Copyright © 2016-2021 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. -/// - -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { PageLink } from '@shared/models/page/page-link'; -import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; -import { Observable } from 'rxjs'; -import { PageData } from '@shared/models/page/page-data'; -import { ChecksumAlgorithm, Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; -import { catchError, map, mergeMap } from 'rxjs/operators'; -import { deepClone } from '@core/utils'; - -@Injectable({ - providedIn: 'root' -}) -export class FirmwareService { - constructor( - private http: HttpClient - ) { - - } - - public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable> { - return this.http.get>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); - } - - public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType, - hasData = true, config?: RequestConfig): Observable> { - const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; - return this.http.get>(url, defaultHttpOptionsFromConfig(config)); - } - - public getFirmware(firmwareId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - - public getFirmwareInfo(firmwareId: string, config?: RequestConfig): Observable { - return this.http.get(`/api/firmware/info/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - - public downloadFirmware(firmwareId: string): Observable { - return this.http.get(`/api/firmware/${firmwareId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( - map((response) => { - const headers = response.headers; - const filename = headers.get('x-filename'); - const contentType = headers.get('content-type'); - const linkElement = document.createElement('a'); - try { - const blob = new Blob([response.body], { type: contentType }); - const url = URL.createObjectURL(blob); - linkElement.setAttribute('href', url); - linkElement.setAttribute('download', filename); - const clickEvent = new MouseEvent('click', - { - view: window, - bubbles: true, - cancelable: false - } - ); - linkElement.dispatchEvent(clickEvent); - return null; - } catch (e) { - throw e; - } - }) - ); - } - - public saveFirmware(firmware: Firmware, config?: RequestConfig): Observable { - if (!firmware.file) { - return this.saveFirmwareInfo(firmware, config); - } - const firmwareInfo = deepClone(firmware); - delete firmwareInfo.file; - delete firmwareInfo.checksum; - delete firmwareInfo.checksumAlgorithm; - return this.saveFirmwareInfo(firmwareInfo, config).pipe( - mergeMap(res => { - return this.uploadFirmwareFile(res.id.id, firmware.file, firmware.checksumAlgorithm, firmware.checksum).pipe( - catchError(() => this.deleteFirmware(res.id.id)) - ); - }) - ); - } - - public saveFirmwareInfo(firmware: FirmwareInfo, config?: RequestConfig): Observable { - return this.http.post('/api/firmware', firmware, defaultHttpOptionsFromConfig(config)); - } - - public uploadFirmwareFile(firmwareId: string, file: File, checksumAlgorithm: ChecksumAlgorithm, - checksum?: string, config?: RequestConfig): Observable { - if (!config) { - config = {}; - } - const formData = new FormData(); - formData.append('file', file); - let url = `/api/firmware/${firmwareId}?checksumAlgorithm=${checksumAlgorithm}`; - if (checksum) { - url += `&checksum=${checksum}`; - } - return this.http.post(url, formData, - defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); - } - - public deleteFirmware(firmwareId: string, config?: RequestConfig) { - return this.http.delete(`/api/firmware/${firmwareId}`, defaultHttpOptionsFromConfig(config)); - } - -} diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts new file mode 100644 index 0000000000..34993e5318 --- /dev/null +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -0,0 +1,123 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; +import { Observable } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { ChecksumAlgorithm, OtaPackage, OtaPackageInfo, OtaUpdateType } from '@shared/models/ota-package.models'; +import { catchError, map, mergeMap } from 'rxjs/operators'; +import { deepClone } from '@core/utils'; + +@Injectable({ + providedIn: 'root' +}) +export class OtaPackageService { + constructor( + private http: HttpClient + ) { + + } + + public getOtaPackages(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/otaPackages${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, + hasData = true, config?: RequestConfig): Observable> { + const url = `/api/otaPackages/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; + return this.http.get>(url, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackage(otaPackageId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/otaPackages/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + + public getOtaPackageInfo(otaPackageId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/otaPackage/info/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + + public downloadOtaPackage(otaPackageId: string): Observable { + return this.http.get(`/api/otaPackage/${otaPackageId}/download`, { responseType: 'arraybuffer', observe: 'response' }).pipe( + map((response) => { + const headers = response.headers; + const filename = headers.get('x-filename'); + const contentType = headers.get('content-type'); + const linkElement = document.createElement('a'); + try { + const blob = new Blob([response.body], { type: contentType }); + const url = URL.createObjectURL(blob); + linkElement.setAttribute('href', url); + linkElement.setAttribute('download', filename); + const clickEvent = new MouseEvent('click', + { + view: window, + bubbles: true, + cancelable: false + } + ); + linkElement.dispatchEvent(clickEvent); + return null; + } catch (e) { + throw e; + } + }) + ); + } + + public saveOtaPackage(otaPackage: OtaPackage, config?: RequestConfig): Observable { + if (!otaPackage.file) { + return this.saveOtaPackageInfo(otaPackage, config); + } + const otaPackageInfo = deepClone(otaPackage); + delete otaPackageInfo.file; + delete otaPackageInfo.checksum; + delete otaPackageInfo.checksumAlgorithm; + return this.saveOtaPackageInfo(otaPackageInfo, config).pipe( + mergeMap(res => { + return this.uploadOtaPackageFile(res.id.id, otaPackage.file, otaPackage.checksumAlgorithm, otaPackage.checksum).pipe( + catchError(() => this.deleteOtaPackage(res.id.id)) + ); + }) + ); + } + + public saveOtaPackageInfo(otaPackageInfo: OtaPackageInfo, config?: RequestConfig): Observable { + return this.http.post('/api/otaPackage', otaPackageInfo, defaultHttpOptionsFromConfig(config)); + } + + public uploadOtaPackageFile(otaPackageId: string, file: File, checksumAlgorithm: ChecksumAlgorithm, + checksum?: string, config?: RequestConfig): Observable { + if (!config) { + config = {}; + } + const formData = new FormData(); + formData.append('file', file); + let url = `/api/otaPackage/${otaPackageId}?checksumAlgorithm=${checksumAlgorithm}`; + if (checksum) { + url += `&checksum=${checksum}`; + } + return this.http.post(url, formData, + defaultHttpUploadOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest)); + } + + public deleteOtaPackage(otaPackageId: string, config?: RequestConfig) { + return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); + } + +} diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 07a6d8f237..806447f112 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -276,9 +276,9 @@ export class MenuService { }, { id: guid(), - name: 'firmware.firmware', + name: 'ota-update.ota-updates', type: 'link', - path: '/firmwares', + path: '/otaUpdates', icon: 'memory' }, { @@ -423,9 +423,9 @@ export class MenuService { path: '/deviceProfiles' }, { - name: 'firmware.firmware', + name: 'ota-update.ota-updates', icon: 'memory', - path: '/firmwares' + path: '/otaUpdates' } ] }, diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index 17caf4f0d7..bd6f92b175 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -66,4 +66,5 @@ {{ 'device-profile.device-profile-required' | translate }} + {{ hint | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts index 3a0ae491da..a2fa8d1ed3 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.ts @@ -91,6 +91,9 @@ export class DeviceProfileAutocompleteComponent implements ControlValueAccessor, @Input() disabled: boolean; + @Input() + hint: string; + @Output() deviceProfileUpdated = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 325bc20419..5fa6c1acdb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -67,18 +67,18 @@ [queueType]="serviceType" formControlName="defaultQueueName"> - - - + - + device-profile.type diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index 6db89d818e..efaa234adb 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -40,7 +40,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { ServiceType } from '@shared/models/queue.models'; import { EntityId } from '@shared/models/id/entity-id'; -import { FirmwareType } from '@shared/models/firmware.models'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; import { DashboardId } from '@shared/models/id/dashboard-id'; @Component({ @@ -71,7 +71,7 @@ export class DeviceProfileComponent extends EntityComponent { deviceProfileId: EntityId; - firmwareTypes = FirmwareType; + otaUpdateType = OtaUpdateType; constructor(protected store: Store, protected translate: TranslateService, diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index c518249b77..4b65083038 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -35,7 +35,7 @@ import { Router } from '@angular/router'; import { BroadcastService } from '@core/services/broadcast.service'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { DeviceProfileService } from '@core/http/device-profile.service'; -import { FirmwareService } from '@core/http/firmware.service'; +import { OtaPackageService } from '@core/http/ota-package.service'; export const ServicesMap = new Map>( [ @@ -59,6 +59,6 @@ export const ServicesMap = new Map>( ['router', Router], ['importExport', ImportExportService], ['deviceProfileService', DeviceProfileService], - ['firmwareService', FirmwareService] + ['otaPackageService', OtaPackageService] ] ); diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index f09437662a..2dd6486eeb 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -101,18 +101,18 @@ device.label - - - + - + diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index d7f21fb2fc..1ed9f784f1 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -34,7 +34,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { Subject } from 'rxjs'; -import { FirmwareType } from '@shared/models/firmware.models'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; @Component({ selector: 'tb-device', @@ -49,7 +49,7 @@ export class DeviceComponent extends EntityComponent { deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge'; - firmwareTypes = FirmwareType; + otaUpdateType = OtaUpdateType; constructor(protected store: Store, protected translate: TranslateService, diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts deleted file mode 100644 index 23efe2117b..0000000000 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts +++ /dev/null @@ -1,115 +0,0 @@ -/// -/// Copyright © 2016-2021 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. -/// - -import { Injectable } from '@angular/core'; -import { Resolve } from '@angular/router'; -import { - DateEntityTableColumn, - EntityTableColumn, - EntityTableConfig -} from '@home/models/entity/entities-table-config.models'; -import { - ChecksumAlgorithmTranslationMap, - Firmware, - FirmwareInfo, - FirmwareTypeTranslationMap -} from '@shared/models/firmware.models'; -import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; -import { TranslateService } from '@ngx-translate/core'; -import { DatePipe } from '@angular/common'; -import { FirmwareService } from '@core/http/firmware.service'; -import { PageLink } from '@shared/models/page/page-link'; -import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; -import { EntityAction } from '@home/models/entity/entity-component.models'; -import { FileSizePipe } from '@shared/pipe/file-size.pipe'; - -@Injectable() -export class FirmwareTableConfigResolve implements Resolve> { - - private readonly config: EntityTableConfig = new EntityTableConfig(); - - constructor(private translate: TranslateService, - private datePipe: DatePipe, - private firmwareService: FirmwareService, - private fileSize: FileSizePipe) { - this.config.entityType = EntityType.FIRMWARE; - this.config.entityComponent = FirmwaresComponent; - this.config.entityTranslations = entityTypeTranslations.get(EntityType.FIRMWARE); - this.config.entityResources = entityTypeResources.get(EntityType.FIRMWARE); - - this.config.entityTitle = (firmware) => firmware ? firmware.title : ''; - - this.config.columns.push( - new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), - new EntityTableColumn('title', 'firmware.title', '25%'), - new EntityTableColumn('version', 'firmware.version', '25%'), - new EntityTableColumn('type', 'firmware.type', '25%', entity => { - return this.translate.instant(FirmwareTypeTranslationMap.get(entity.type)); - }), - new EntityTableColumn('fileName', 'firmware.file-name', '25%'), - new EntityTableColumn('dataSize', 'firmware.file-size', '70px', entity => { - return this.fileSize.transform(entity.dataSize || 0); - }), - new EntityTableColumn('checksum', 'firmware.checksum', '540px', entity => { - return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; - }, () => ({}), false) - ); - - this.config.cellActionDescriptors.push( - { - name: this.translate.instant('firmware.download'), - icon: 'file_download', - isEnabled: (firmware) => firmware.hasData, - onAction: ($event, entity) => this.exportFirmware($event, entity) - } - ); - - this.config.deleteEntityTitle = firmware => this.translate.instant('firmware.delete-firmware-title', - { firmwareTitle: firmware.title }); - this.config.deleteEntityContent = () => this.translate.instant('firmware.delete-firmware-text'); - this.config.deleteEntitiesTitle = count => this.translate.instant('firmware.delete-firmwares-title', {count}); - this.config.deleteEntitiesContent = () => this.translate.instant('firmware.delete-firmwares-text'); - - this.config.entitiesFetchFunction = pageLink => this.firmwareService.getFirmwares(pageLink); - this.config.loadEntity = id => this.firmwareService.getFirmwareInfo(id.id); - this.config.saveEntity = firmware => this.firmwareService.saveFirmware(firmware); - this.config.deleteEntity = id => this.firmwareService.deleteFirmware(id.id); - - this.config.onEntityAction = action => this.onFirmwareAction(action); - } - - resolve(): EntityTableConfig { - this.config.tableTitle = this.translate.instant('firmware.firmware'); - return this.config; - } - - exportFirmware($event: Event, firmware: FirmwareInfo) { - if ($event) { - $event.stopPropagation(); - } - this.firmwareService.downloadFirmware(firmware.id.id).subscribe(); - } - - onFirmwareAction(action: EntityAction): boolean { - switch (action.action) { - case 'uploadFirmware': - this.exportFirmware(action.event, action.entity); - return true; - } - return false; - } - -} diff --git a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts index 050b71db83..834321fc49 100644 --- a/ui-ngx/src/app/modules/home/pages/home-pages.module.ts +++ b/ui-ngx/src/app/modules/home/pages/home-pages.module.ts @@ -35,7 +35,7 @@ import { modulesMap } from '../../common/modules-map'; import { DeviceProfileModule } from './device-profile/device-profile.module'; import { ApiUsageModule } from '@home/pages/api-usage/api-usage.module'; import { EdgeModule } from '@home/pages/edge/edge.module'; -import { FirmwareModule } from '@home/pages/firmware/firmware.module'; +import { OtaUpdateModule } from '@home/pages/ota-update/ota-update.module'; @NgModule({ exports: [ @@ -55,7 +55,7 @@ import { FirmwareModule } from '@home/pages/firmware/firmware.module'; DashboardModule, AuditLogModule, ApiUsageModule, - FirmwareModule, + OtaUpdateModule, UserModule ], providers: [ diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts similarity index 78% rename from ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts index 688f3bfc1f..6945244c47 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-routing.module.ts @@ -18,22 +18,22 @@ import { RouterModule, Routes } from '@angular/router'; import { EntitiesTableComponent } from '@home/components/entity/entities-table.component'; import { Authority } from '@shared/models/authority.enum'; import { NgModule } from '@angular/core'; -import { FirmwareTableConfigResolve } from '@home/pages/firmware/firmware-table-config.resolve'; +import { OtaUpdateTableConfigResolve } from '@home/pages/ota-update/ota-update-table-config.resolve'; const routes: Routes = [ { - path: 'firmwares', + path: 'otaUpdates', component: EntitiesTableComponent, data: { auth: [Authority.TENANT_ADMIN], - title: 'firmware.firmware', + title: 'ota-update.ota-updates', breadcrumb: { - label: 'firmware.firmware', + label: 'ota-update.ota-updates', icon: 'memory' } }, resolve: { - entitiesTableConfig: FirmwareTableConfigResolve + entitiesTableConfig: OtaUpdateTableConfigResolve } } ]; @@ -42,7 +42,7 @@ const routes: Routes = [ imports: [RouterModule.forChild(routes)], exports: [RouterModule], providers: [ - FirmwareTableConfigResolve + OtaUpdateTableConfigResolve ] }) -export class FirmwareRoutingModule{ } +export class OtaUpdateRoutingModule { } diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts new file mode 100644 index 0000000000..b22d43da46 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -0,0 +1,116 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; +import { + DateEntityTableColumn, + EntityTableColumn, + EntityTableConfig +} from '@home/models/entity/entities-table-config.models'; +import { + ChecksumAlgorithmTranslationMap, + OtaPackage, + OtaPackageInfo, + OtaUpdateTypeTranslationMap +} from '@shared/models/ota-package.models'; +import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; +import { TranslateService } from '@ngx-translate/core'; +import { DatePipe } from '@angular/common'; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { PageLink } from '@shared/models/page/page-link'; +import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; +import { EntityAction } from '@home/models/entity/entity-component.models'; +import { FileSizePipe } from '@shared/pipe/file-size.pipe'; + +@Injectable() +export class OtaUpdateTableConfigResolve implements Resolve> { + + private readonly config: EntityTableConfig = + new EntityTableConfig(); + + constructor(private translate: TranslateService, + private datePipe: DatePipe, + private otaPackageService: OtaPackageService, + private fileSize: FileSizePipe) { + this.config.entityType = EntityType.OTA_PACKAGE; + this.config.entityComponent = OtaUpdateComponent; + this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE); + this.config.entityResources = entityTypeResources.get(EntityType.OTA_PACKAGE); + + this.config.entityTitle = (otaPackage) => otaPackage ? otaPackage.title : ''; + + this.config.columns.push( + new DateEntityTableColumn('createdTime', 'common.created-time', this.datePipe, '150px'), + new EntityTableColumn('title', 'ota-update.title', '25%'), + new EntityTableColumn('version', 'ota-update.version', '25%'), + new EntityTableColumn('type', 'ota-update.package-type', '25%', entity => { + return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); + }), + new EntityTableColumn('fileName', 'ota-update.file-name', '25%'), + new EntityTableColumn('dataSize', 'ota-update.file-size', '70px', entity => { + return this.fileSize.transform(entity.dataSize || 0); + }), + new EntityTableColumn('checksum', 'ota-update.checksum', '540px', entity => { + return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; + }, () => ({}), false) + ); + + this.config.cellActionDescriptors.push( + { + name: this.translate.instant('ota-update.download'), + icon: 'file_download', + isEnabled: (otaPackage) => otaPackage.hasData, + onAction: ($event, entity) => this.exportPackage($event, entity) + } + ); + + this.config.deleteEntityTitle = otaPackage => this.translate.instant('ota-update.delete-ota-update-title', + { title: otaPackage.title }); + this.config.deleteEntityContent = () => this.translate.instant('ota-update.delete-ota-update-text'); + this.config.deleteEntitiesTitle = count => this.translate.instant('ota-update.delete-ota-updates-title', {count}); + this.config.deleteEntitiesContent = () => this.translate.instant('ota-update.delete-ota-updates-text'); + + this.config.entitiesFetchFunction = pageLink => this.otaPackageService.getOtaPackages(pageLink); + this.config.loadEntity = id => this.otaPackageService.getOtaPackageInfo(id.id); + this.config.saveEntity = otaPackage => this.otaPackageService.saveOtaPackage(otaPackage); + this.config.deleteEntity = id => this.otaPackageService.deleteOtaPackage(id.id); + + this.config.onEntityAction = action => this.onPackageAction(action); + } + + resolve(): EntityTableConfig { + this.config.tableTitle = this.translate.instant('ota-update.packages-repository'); + return this.config; + } + + exportPackage($event: Event, otaPackageInfo: OtaPackageInfo) { + if ($event) { + $event.stopPropagation(); + } + this.otaPackageService.downloadOtaPackage(otaPackageInfo.id.id).subscribe(); + } + + onPackageAction(action: EntityAction): boolean { + switch (action.action) { + case 'uploadPackage': + this.exportPackage(action.event, action.entity); + return true; + } + return false; + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html similarity index 60% rename from ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index b608604d2f..77a68f72e5 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -18,114 +18,112 @@
-
+
-
- firmware.warning-after-save-no-edit +
- firmware.title + ota-update.title - {{ 'firmware.title-required' | translate }} + {{ 'ota-update.title-required' | translate }} - firmware.version + ota-update.version - {{ 'firmware.version-required' | translate }} + {{ 'ota-update.version-required' | translate }}
-
- - firmware.type - - - - {{ firmwareTypeTranslationMap.get(firmwareType) | translate }} - - - - - -
+ + + + ota-update.package-type + + + {{ otaUpdateTypeTranslationMap.get(packageType) | translate }} + + + +
ota-update.warning-after-save-no-edit
- firmware.checksum-algorithm - - + ota-update.checksum-algorithm + {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} - firmware.checksum + ota-update.checksum
-
+
+ dropLabel="{{'ota-update.drop-file' | translate}}">
- firmware.file-name + ota-update.file-name - firmware.file-size-bytes + ota-update.file-size-bytes - firmware.content-type + ota-update.content-type
- firmware.description + ota-update.description
diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts similarity index 74% rename from ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index e2283a588f..fd4cd7ae27 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -25,29 +25,29 @@ import { EntityComponent } from '@home/components/entity/entity.component'; import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, - Firmware, - FirmwareType, - FirmwareTypeTranslationMap -} from '@shared/models/firmware.models'; + OtaPackage, + OtaUpdateType, + OtaUpdateTypeTranslationMap +} from '@shared/models/ota-package.models'; import { ActionNotificationShow } from '@core/notification/notification.actions'; @Component({ - selector: 'tb-firmware', - templateUrl: './firmwares.component.html' + selector: 'tb-ota-update', + templateUrl: './ota-update.component.html' }) -export class FirmwaresComponent extends EntityComponent implements OnInit, OnDestroy { +export class OtaUpdateComponent extends EntityComponent implements OnInit, OnDestroy { private destroy$ = new Subject(); checksumAlgorithms = Object.values(ChecksumAlgorithm); checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap; - firmwareTypes = Object.values(FirmwareType); - firmwareTypeTranslationMap = FirmwareTypeTranslationMap; + packageTypes = Object.values(OtaUpdateType); + otaUpdateTypeTranslationMap = OtaUpdateTypeTranslationMap; constructor(protected store: Store, protected translate: TranslateService, - @Inject('entity') protected entityValue: Firmware, - @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, + @Inject('entity') protected entityValue: OtaPackage, + @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig, public fb: FormBuilder) { super(store, fb, entityValue, entitiesTableConfigValue); } @@ -66,12 +66,12 @@ export class FirmwaresComponent extends EntityComponent implements OnI } } - buildForm(entity: Firmware): FormGroup { + buildForm(entity: OtaPackage): FormGroup { const form = this.fb.group({ title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], - type: [entity?.type ? entity.type : FirmwareType.FIRMWARE, [Validators.required]], - deviceProfileId: [entity ? entity.deviceProfileId : null], + type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required], + deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)], additionalInfo: this.fb.group( @@ -90,7 +90,7 @@ export class FirmwaresComponent extends EntityComponent implements OnI return form; } - updateForm(entity: Firmware) { + updateForm(entity: OtaPackage) { this.entityForm.patchValue({ title: entity.title, version: entity.version, @@ -105,12 +105,18 @@ export class FirmwaresComponent extends EntityComponent implements OnI description: entity.additionalInfo ? entity.additionalInfo.description : '' } }); + if (!this.isAdd && this.entityForm.enabled) { + this.entityForm.disable({emitEvent: false}); + this.entityForm.get('additionalInfo').enable({emitEvent: false}); + // this.entityForm.get('dataSize').disable({emitEvent: false}); + // this.entityForm.get('contentType').disable({emitEvent: false}); + } } - onFirmwareIdCopied() { + onPackageIdCopied() { this.store.dispatch(new ActionNotificationShow( { - message: this.translate.instant('firmware.idCopiedMessage'), + message: this.translate.instant('ota-update.idCopiedMessage'), type: 'success', duration: 750, verticalPosition: 'bottom', @@ -118,10 +124,10 @@ export class FirmwaresComponent extends EntityComponent implements OnI })); } - onFirmwareChecksumCopied() { + onPackageChecksumCopied() { this.store.dispatch(new ActionNotificationShow( { - message: this.translate.instant('firmware.checksum-copied-message'), + message: this.translate.instant('ota-update.checksum-copied-message'), type: 'success', duration: 750, verticalPosition: 'bottom', diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts similarity index 79% rename from ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts rename to ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts index ab1b8343b2..58beef85ec 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware.module.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.module.ts @@ -18,18 +18,18 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { SharedModule } from '@shared/shared.module'; import { HomeComponentsModule } from '@home/components/home-components.module'; -import { FirmwareRoutingModule } from '@home/pages/firmware/firmware-routing.module'; -import { FirmwaresComponent } from '@home/pages/firmware/firmwares.component'; +import { OtaUpdateRoutingModule } from '@home/pages/ota-update/ota-update-routing.module'; +import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; @NgModule({ declarations: [ - FirmwaresComponent + OtaUpdateComponent ], imports: [ CommonModule, SharedModule, HomeComponentsModule, - FirmwareRoutingModule + OtaUpdateRoutingModule ] }) -export class FirmwareModule { } +export class OtaUpdateModule { } diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html similarity index 62% rename from ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html rename to ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html index 7bb434e7d1..51c291006d 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.html @@ -15,40 +15,41 @@ limitations under the License. --> - + - - - + #packageAutocomplete="matAutocomplete" + [displayWith]="displayPackageFn"> + + - +
- {{ notFoundFirmware | translate }} + {{ notFoundPackage | translate }}
- {{ translate.get(notMatchingFirmware, + {{ translate.get(notMatchingPackage, {entity: truncate.transform(searchText, true, 6, '...')}) | async }}
- + {{ requiredErrorText | translate }} + {{ hintText | translate }}
diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts similarity index 59% rename from ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts rename to ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index a3aeafbd5e..25df909a8c 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -28,29 +28,29 @@ import { BaseData } from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service'; import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; -import { FirmwareService } from '@core/http/firmware.service'; +import { OtaPackageInfo, OtaUpdateTranslation, OtaUpdateType } from '@shared/models/ota-package.models'; +import { OtaPackageService } from '@core/http/ota-package.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; @Component({ - selector: 'tb-firmware-autocomplete', - templateUrl: './firmware-autocomplete.component.html', + selector: 'tb-ota-package-autocomplete', + templateUrl: './ota-package-autocomplete.component.html', styleUrls: [], providers: [{ provide: NG_VALUE_ACCESSOR, - useExisting: forwardRef(() => FirmwareAutocompleteComponent), + useExisting: forwardRef(() => OtaPackageAutocompleteComponent), multi: true }] }) -export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnInit { +export class OtaPackageAutocompleteComponent implements ControlValueAccessor, OnInit { - firmwareFormGroup: FormGroup; + otaPackageFormGroup: FormGroup; modelValue: string | EntityId | null; @Input() - type = FirmwareType.FIRMWARE; + type = OtaUpdateType.FIRMWARE; @Input() deviceProfileId: string; @@ -78,42 +78,24 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn @Input() disabled: boolean; - @ViewChild('firmwareInput', {static: true}) firmwareInput: ElementRef; - @ViewChild('firmwareInput', {read: MatAutocompleteTrigger}) firmwareAutocomplete: MatAutocompleteTrigger; + @ViewChild('packageInput', {static: true}) packageInput: ElementRef; - filteredFirmwares: Observable>; + filteredPackages: Observable>; searchText = ''; private dirty = false; - private firmwareTypeTranslation = new Map( - [ - [FirmwareType.FIRMWARE, { - label: 'firmware.firmware', - required: 'firmware.firmware-required', - noFound: 'firmware.no-firmware-text', - noMatching: 'firmware.no-firmware-matching' - }], - [FirmwareType.SOFTWARE, { - label: 'firmware.software', - required: 'firmware.software-required', - noFound: 'firmware.no-software-text', - noMatching: 'firmware.no-software-matching' - }] - ] - ); - private propagateChange = (v: any) => { }; constructor(private store: Store, public translate: TranslateService, public truncate: TruncatePipe, private entityService: EntityService, - private firmwareService: FirmwareService, + private otaPackageService: OtaPackageService, private fb: FormBuilder) { - this.firmwareFormGroup = this.fb.group({ - firmwareId: [null] + this.otaPackageFormGroup = this.fb.group({ + packageId: [null] }); } @@ -125,7 +107,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } ngOnInit() { - this.filteredFirmwares = this.firmwareFormGroup.get('firmwareId').valueChanges + this.filteredPackages = this.otaPackageFormGroup.get('packageId').valueChanges .pipe( tap(value => { let modelValue; @@ -140,7 +122,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } }), map(value => value ? (typeof value === 'string' ? value : value.title) : ''), - mergeMap(name => this.fetchFirmware(name)), + mergeMap(name => this.fetchPackages(name)), share() ); } @@ -149,7 +131,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } getCurrentEntity(): BaseData | null { - const currentRuleChain = this.firmwareFormGroup.get('firmwareId').value; + const currentRuleChain = this.otaPackageFormGroup.get('packageId').value; if (currentRuleChain && typeof currentRuleChain !== 'string') { return currentRuleChain as BaseData; } else { @@ -160,9 +142,9 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (this.disabled) { - this.firmwareFormGroup.disable({emitEvent: false}); + this.otaPackageFormGroup.disable({emitEvent: false}); } else { - this.firmwareFormGroup.enable({emitEvent: false}); + this.otaPackageFormGroup.enable({emitEvent: false}); } } @@ -173,21 +155,21 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn writeValue(value: string | EntityId | null): void { this.searchText = ''; if (value != null && value !== '') { - let firmwareId = ''; + let packageId = ''; if (typeof value === 'string') { - firmwareId = value; + packageId = value; } else if (value.entityType && value.id) { - firmwareId = value.id; + packageId = value.id; } - if (firmwareId !== '') { - this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe( + if (packageId !== '') { + this.entityService.getEntity(EntityType.OTA_PACKAGE, packageId, {ignoreLoading: true, ignoreErrors: true}).subscribe( (entity) => { this.modelValue = this.useFullEntityId ? entity.id : entity.id.id; - this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue(entity, {emitEvent: false}); }, () => { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); if (value !== null) { this.propagateChange(this.modelValue); } @@ -195,25 +177,25 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn ); } else { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); this.propagateChange(null); } } else { this.modelValue = null; - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); } this.dirty = true; } onFocus() { if (this.dirty) { - this.firmwareFormGroup.get('firmwareId').updateValueAndValidity({onlySelf: true, emitEvent: true}); + this.otaPackageFormGroup.get('packageId').updateValueAndValidity({onlySelf: true, emitEvent: true}); this.dirty = false; } } reset() { - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: false}); } updateView(value: string | null) { @@ -223,47 +205,51 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } } - displayFirmwareFn(firmware?: FirmwareInfo): string | undefined { - return firmware ? `${firmware.title} (${firmware.version})` : undefined; + displayPackageFn(packageInfo?: OtaPackageInfo): string | undefined { + return packageInfo ? `${packageInfo.title} (${packageInfo.version})` : undefined; } - fetchFirmware(searchText?: string): Observable> { + fetchPackages(searchText?: string): Observable> { this.searchText = searchText; const pageLink = new PageLink(50, 0, searchText, { property: 'title', direction: Direction.ASC }); - return this.firmwareService.getFirmwaresInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, + return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, true, {ignoreLoading: true}).pipe( map((data) => data && data.data.length ? data.data : null) ); } clear() { - this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: true}); + this.otaPackageFormGroup.get('packageId').patchValue('', {emitEvent: true}); setTimeout(() => { - this.firmwareInput.nativeElement.blur(); - this.firmwareInput.nativeElement.focus(); + this.packageInput.nativeElement.blur(); + this.packageInput.nativeElement.focus(); }, 0); } get placeholderText(): string { - return this.labelText || this.firmwareTypeTranslation.get(this.type).label; + return this.labelText || OtaUpdateTranslation.get(this.type).label; } get requiredErrorText(): string { - return this.requiredText || this.firmwareTypeTranslation.get(this.type).required; + return this.requiredText || OtaUpdateTranslation.get(this.type).required; + } + + get notFoundPackage(): string { + return OtaUpdateTranslation.get(this.type).noFound; } - get notFoundFirmware(): string { - return this.firmwareTypeTranslation.get(this.type).noFound; + get notMatchingPackage(): string { + return OtaUpdateTranslation.get(this.type).noMatching; } - get notMatchingFirmware(): string { - return this.firmwareTypeTranslation.get(this.type).noMatching; + get hintText(): string { + return OtaUpdateTranslation.get(this.type).hint; } - firmwareTitleText(firmware: FirmwareInfo): string { - return `${firmware.title} (${firmware.version})`; + packageTitleText(firpackageInfomware: OtaPackageInfo): string { + return `${firpackageInfomware.title} (${firpackageInfomware.version})`; } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 44327e1c2c..b72e2b196d 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -121,6 +121,7 @@ export const HelpLinks = { entitiesImport: helpBaseUrl + '/docs/user-guide/bulk-provisioning', rulechains: helpBaseUrl + '/docs/user-guide/ui/rule-chains', dashboards: helpBaseUrl + '/docs/user-guide/ui/dashboards', + otaUpdates: helpBaseUrl + '/docs/user-guide/ui/ota-updates', widgetsBundles: helpBaseUrl + '/docs/user-guide/ui/widget-library#bundles', widgetsConfig: helpBaseUrl + '/docs/user-guide/ui/dashboards#widget-configuration', widgetsConfigTimeseries: helpBaseUrl + '/docs/user-guide/ui/dashboards#timeseries', diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index fe7868d584..9b13c4dc72 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -27,7 +27,7 @@ import { KeyFilter } from '@shared/models/query/query.models'; import { TimeUnit } from '@shared/models/time/time.models'; import * as _moment from 'moment'; import { AbstractControl, ValidationErrors } from '@angular/forms'; -import { FirmwareId } from '@shared/models/id/firmware-id'; +import { OtaPackageId } from '@shared/models/id/ota-package-id'; import { DashboardId } from '@shared/models/id/dashboard-id'; export enum DeviceProfileType { @@ -500,8 +500,8 @@ export interface DeviceProfile extends BaseData { defaultRuleChainId?: RuleChainId; defaultDashboardId?: DashboardId; defaultQueueName?: string; - firmwareId?: FirmwareId; - softwareId?: FirmwareId; + firmwareId?: OtaPackageId; + softwareId?: OtaPackageId; profileData: DeviceProfileData; } @@ -563,8 +563,8 @@ export interface Device extends BaseData { name: string; type: string; label: string; - firmwareId?: FirmwareId; - softwareId?: FirmwareId; + firmwareId?: OtaPackageId; + softwareId?: OtaPackageId; deviceProfileId?: DeviceProfileId; deviceData?: DeviceData; additionalInfo?: any; diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index ae2382e0ea..d088cbc88a 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -35,7 +35,7 @@ export enum EntityType { WIDGET_TYPE = 'WIDGET_TYPE', API_USAGE_STATE = 'API_USAGE_STATE', TB_RESOURCE = 'TB_RESOURCE', - FIRMWARE = 'FIRMWARE' + OTA_PACKAGE = 'OTA_PACKAGE' } export enum AliasEntityType { @@ -296,14 +296,14 @@ export const entityTypeTranslations = new Map( +export const OtaUpdateTypeTranslationMap = new Map( [ - [FirmwareType.FIRMWARE, 'firmware.types.firmware'], - [FirmwareType.SOFTWARE, 'firmware.types.software'] + [OtaUpdateType.FIRMWARE, 'ota-update.types.firmware'], + [OtaUpdateType.SOFTWARE, 'ota-update.types.software'] ] ); -export interface FirmwareInfo extends BaseData { +export interface OtaUpdateTranslation { + label: string; + required: string; + noFound: string; + noMatching: string; + hint: string; +} + +export const OtaUpdateTranslation = new Map( + [ + [OtaUpdateType.FIRMWARE, { + label: 'ota-update.assign-firmware', + required: 'ota-update.assign-firmware-required', + noFound: 'ota-update.no-firmware-text', + noMatching: 'ota-update.no-firmware-matching', + hint: 'ota-update.chose-firmware-distributed-device' + }], + [OtaUpdateType.SOFTWARE, { + label: 'ota-update.assign-software', + required: 'ota-update.assign-software-required', + noFound: 'ota-update.no-software-text', + noMatching: 'ota-update.no-software-matching', + hint: 'ota-update.chose-software-distributed-device' + }] + ] +); + +export interface OtaPackageInfo extends BaseData { tenantId?: TenantId; - type: FirmwareType; + type: OtaUpdateType; deviceProfileId?: DeviceProfileId; title?: string; version?: string; @@ -68,7 +95,7 @@ export interface FirmwareInfo extends BaseData { additionalInfo?: any; } -export interface Firmware extends FirmwareInfo { +export interface OtaPackage extends OtaPackageInfo { file?: File; data: string; } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 925b297e77..5e71d4c44a 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -141,7 +141,7 @@ import { FileSizePipe } from '@shared/pipe/file-size.pipe'; import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component'; import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; -import { FirmwareAutocompleteComponent } from '@shared/components/firmware/firmware-autocomplete.component'; +import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ota-package-autocomplete.component'; import { MAT_DATE_LOCALE } from '@angular/material/core'; @NgModule({ @@ -239,7 +239,7 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; HistorySelectorComponent, EntityGatewaySelectComponent, ContactComponent, - FirmwareAutocompleteComponent, + OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent ], imports: [ @@ -411,7 +411,7 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; HistorySelectorComponent, EntityGatewaySelectComponent, ContactComponent, - FirmwareAutocompleteComponent, + OtaPackageAutocompleteComponent, WidgetsBundleSearchComponent ] }) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c04da7b2e7..204abf4f8c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1516,7 +1516,7 @@ "list-of-edges": "{ count, plural, 1 {One edge} other {List of # edges} }", "edge-name-starts-with": "Edges whose names start with '{{prefix}}'", "type-tb-resource": "Resource", - "type-firmware": "Firmware" + "type-ota-package": "OTA package" }, "entity-field": { "created-time": "Created time", @@ -1927,51 +1927,6 @@ "inherit-owner": "Inherit from owner", "source-attribute-not-set": "If source attribute isn't set" }, - "firmware": { - "add": "Add firmware", - "checksum": "Checksum", - "checksum-copied-message": "Firmware checksum has been copied to clipboard", - "checksum-required": "Checksum is required.", - "checksum-algorithm": "Checksum algorithm", - "content-type": "Content type", - "copy-checksum": "Copy checksum", - "copyId": "Copy firmware Id", - "description": "Description", - "delete": "Delete firmware", - "delete-firmware-text": "Be careful, after the confirmation the firmware will become unrecoverable.", - "delete-firmware-title": "Are you sure you want to delete the firmware '{{firmwareTitle}}'?", - "delete-firmwares-action-title": "Delete { count, plural, 1 {1 firmware} other {# firmwares} }", - "delete-firmwares-text": "Be careful, after the confirmation all selected resources will be removed.", - "delete-firmwares-title": "Are you sure you want to delete { count, plural, 1 {1 firmware} other {# firmwares} }?", - "download": "Download firmware", - "drop-file": "Drop a firmware file or click to select a file to upload.", - "empty": "Firmware is empty", - "idCopiedMessage": "Firmware Id has been copied to clipboard", - "no-firmware-matching": "No firmware matching '{{entity}}' were found.", - "no-firmware-text": "No firmwares found", - "no-software-matching": "No sowtware matching '{{entity}}' were found.", - "no-software-text": "No software found", - "file-name": "File name", - "file-size": "File size", - "file-size-bytes": "File size in bytes", - "firmware": "Firmware", - "firmware-details": "Firmware details", - "firmware-required": "Firmware is required.", - "search": "Search firmwares", - "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected", - "software": "Software", - "software-required": "Software is required.", - "title": "Title", - "title-required": "Title is required.", - "type": "Firmware type", - "types": { - "firmware": "Firmware", - "software": "Software" - }, - "version": "Version", - "version-required": "Version is required.", - "warning-after-save-no-edit": "Once the firmware is saved, it will not be possible to change the title and version fields." - }, "fullscreen": { "expand": "Expand to fullscreen", "exit": "Exit fullscreen", @@ -2191,6 +2146,55 @@ "or": "or", "error": "Login error" }, + "ota-update": { + "add": "Add package", + "assign-firmware": "Assigned firmware", + "assign-firmware-required": "Assigned firmware is required", + "assign-software": "Assigned software", + "assign-software-required": "Assigned software is required", + "checksum": "Checksum", + "checksum-algorithm": "Checksum algorithm", + "checksum-copied-message": "Package checksum has been copied to clipboard", + "chose-compatible-device-profile": "Choose compatible device profile", + "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", + "chose-software-distributed-device": "Choose software that will be distributed to the devices", + "content-type": "Content type", + "copy-checksum": "Copy checksum", + "copyId": "Copy package Id", + "idCopiedMessage": "Package Id has been copied to clipboard", + "description": "Description", + "delete": "Delete package", + "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", + "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", + "delete-ota-updates-text": "Be careful, after the confirmation all selected OTA updates will be removed.", + "delete-ota-updates-title": "Are you sure you want to delete { count, plural, 1 {1 OTA update} other {# OTA updates} }?", + "drop-file": "Drop a package file or click to select a file to upload.", + "download": "Download package", + "file-name": "File name", + "file-size": "File size", + "file-size-bytes": "File size in bytes", + "no-packages-text": "No packages found", + "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "no-firmware-matching": "No compatible Firmware OTA Update packages matching '{{entity}}' were found.", + "no-software-text": "No compatible Software OTA Update packages provisioned.", + "no-software-matching": "No compatible Software OTA Update packages matching '{{entity}}' were found.", + "search": "Search packages", + "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", + "ota-update": "OTA update", + "ota-update-details": "OTA update details", + "ota-updates": "OTA updates", + "package-type": "Package type", + "packages-repository": "Packages repository", + "title": "Title", + "title-required": "Title is required.", + "types": { + "firmware": "Firmware", + "software": "Software" + }, + "version": "Version", + "version-required": "Version is required.", + "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." + }, "position": { "top": "Top", "bottom": "Bottom", From 078fdce491514d6fc38c18bd9b36c8f28d958e3f Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 31 May 2021 19:50:39 +0300 Subject: [PATCH 21/75] firmware dashboard improvements --- .../data/json/demo/dashboards/firmware.json | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/application/src/main/data/json/demo/dashboards/firmware.json b/application/src/main/data/json/demo/dashboards/firmware.json index 6e8d18cd98..4711e34702 100644 --- a/application/src/main/data/json/demo/dashboards/firmware.json +++ b/application/src/main/data/json/demo/dashboards/firmware.json @@ -1,5 +1,6 @@ { "title": "Firmware", + "image": null, "configuration": { "description": "", "widgets": { @@ -247,7 +248,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -257,7 +258,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1021,7 +1022,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1031,7 +1032,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1297,7 +1298,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1307,7 +1308,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1573,7 +1574,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1583,7 +1584,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { @@ -1849,7 +1850,7 @@ "name": "Edit firmware", "icon": "edit", "type": "customPretty", - "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", "customCss": "", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], @@ -1859,7 +1860,7 @@ "name": "Download firware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet firmwareService = $injector.get(widgetContext.servicesMap.get('firmwareService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n firmwareService.downloadFirmware(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n firmwareService.downloadFirmware(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { From 7da03c1b50d07e314a58b936574a988abb633907 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Tue, 1 Jun 2021 10:52:28 +0300 Subject: [PATCH 22/75] remove transaction from TbLwM2mRedisRegistrationStore --- .../lwm2m/server/DefaultLwM2MTransportMsgHandler.java | 1 - .../server/store/TbLwM2mRedisRegistrationStore.java | 9 +++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index abe2a50855..2ae1be66f3 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -247,7 +247,6 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect */ public void unReg(Registration registration, Collection observations) { - log.error("Client unRegistration -> test", new RuntimeException()); unRegistrationExecutor.submit(() -> { try { this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java index 973124b4e9..365b92bf66 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/TbLwM2mRedisRegistrationStore.java @@ -337,23 +337,24 @@ public class TbLwM2mRedisRegistrationStore implements CaliforniumRegistrationSto } } + //TODO: JedisCluster didn't implement Transaction, maybe should use some advanced key creation strategies private void removeAddrIndex(RedisConnection connection, Registration registration) { // Watch the key to remove. byte[] regAddrKey = toRegAddrKey(registration.getSocketAddress()); - connection.watch(regAddrKey); +// connection.watch(regAddrKey); byte[] epFromAddr = connection.get(regAddrKey); // Delete the key if needed. if (Arrays.equals(epFromAddr, registration.getEndpoint().getBytes(UTF_8))) { // Try to delete the key - connection.multi(); +// connection.multi(); connection.del(regAddrKey); - connection.exec(); +// connection.exec(); // if transaction failed this is not an issue as the socket address is probably reused and we don't neeed to // delete it anymore. } else { // the key must not be deleted. - connection.unwatch(); +// connection.unwatch(); } } From e0f04091b89d5ee2490df2ee773f331e6fee578b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 1 Jun 2021 11:36:25 +0300 Subject: [PATCH 23/75] UI: Change order translate ota-package --- .../src/assets/locale/locale.constant-en_US.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 204abf4f8c..6966888ea3 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2161,30 +2161,30 @@ "content-type": "Content type", "copy-checksum": "Copy checksum", "copyId": "Copy package Id", - "idCopiedMessage": "Package Id has been copied to clipboard", - "description": "Description", "delete": "Delete package", "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", "delete-ota-updates-text": "Be careful, after the confirmation all selected OTA updates will be removed.", "delete-ota-updates-title": "Are you sure you want to delete { count, plural, 1 {1 OTA update} other {# OTA updates} }?", - "drop-file": "Drop a package file or click to select a file to upload.", + "description": "Description", "download": "Download package", + "drop-file": "Drop a package file or click to select a file to upload.", "file-name": "File name", "file-size": "File size", "file-size-bytes": "File size in bytes", - "no-packages-text": "No packages found", - "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "idCopiedMessage": "Package Id has been copied to clipboard", "no-firmware-matching": "No compatible Firmware OTA Update packages matching '{{entity}}' were found.", - "no-software-text": "No compatible Software OTA Update packages provisioned.", + "no-firmware-text": "No compatible Firmware OTA Update packages provisioned.", + "no-packages-text": "No packages found", "no-software-matching": "No compatible Software OTA Update packages matching '{{entity}}' were found.", - "search": "Search packages", - "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", + "no-software-text": "No compatible Software OTA Update packages provisioned.", "ota-update": "OTA update", "ota-update-details": "OTA update details", "ota-updates": "OTA updates", "package-type": "Package type", "packages-repository": "Packages repository", + "search": "Search packages", + "selected-package": "{ count, plural, 1 {1 package} other {# packages} } selected", "title": "Title", "title-required": "Title is required.", "types": { From 51d14efe6ac36e4c531d5b86ba82633435333d75 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Wed, 2 Jun 2021 11:40:20 +0300 Subject: [PATCH 24/75] fixed OtaPackage data cache --- .../server/controller/DeviceController.java | 8 ++-- .../src/main/resources/thingsboard.yml | 3 ++ .../cache/ota/CaffeineOtaPackageCache.java | 7 +-- .../cache/ota/RedisOtaPackageDataCache.java | 3 +- .../server/common/data/CacheConstants.java | 1 + .../server/dao/ota/BaseOtaPackageService.java | 15 ++----- .../service/BaseOtaPackageServiceTest.java | 44 +------------------ .../resources/application-test.properties | 3 ++ 8 files changed, 23 insertions(+), 61 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java index 90094e88c1..b13393515c 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceController.java @@ -782,15 +782,15 @@ public class DeviceController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/devices/count", method = RequestMethod.GET) + @RequestMapping(value = "/devices/count/{otaPackageType}", method = RequestMethod.GET) @ResponseBody - public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@RequestParam(required = false) String otaPackageType, - @RequestParam(required = false) String deviceProfileId) throws ThingsboardException { + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(@PathVariable("otaPackageType") String otaPackageType, + @RequestParam String deviceProfileId) throws ThingsboardException { checkParameter("OtaPackageType", otaPackageType); checkParameter("DeviceProfileId", deviceProfileId); try { return deviceService.countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage( - getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(deviceProfileId)); + getCurrentUser().getTenantId(), new DeviceProfileId(UUID.fromString(deviceProfileId)), OtaPackageType.valueOf(otaPackageType)); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index ae628bf9f6..de1f3cd3af 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -374,6 +374,9 @@ caffeine: otaPackages: timeToLiveInMinutes: 60 maxSize: 10 + otaPackagesData: + timeToLiveInMinutes: 60 + maxSize: 10 edges: timeToLiveInMinutes: 1440 maxSize: 0 diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java index a864fc6dba..8b1f0804f3 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/CaffeineOtaPackageCache.java @@ -21,6 +21,7 @@ import org.springframework.cache.CacheManager; import org.springframework.stereotype.Service; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) @@ -36,7 +37,7 @@ public class CaffeineOtaPackageCache implements OtaPackageDataCache { @Override public byte[] get(String key, int chunkSize, int chunk) { - byte[] data = cacheManager.getCache(OTA_PACKAGE_CACHE).get(key, byte[].class); + byte[] data = cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).get(key, byte[].class); if (chunkSize < 1) { return data; @@ -58,11 +59,11 @@ public class CaffeineOtaPackageCache implements OtaPackageDataCache { @Override public void put(String key, byte[] value) { - cacheManager.getCache(OTA_PACKAGE_CACHE).putIfAbsent(key, value); + cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).putIfAbsent(key, value); } @Override public void evict(String key) { - cacheManager.getCache(OTA_PACKAGE_CACHE).evict(key); + cacheManager.getCache(OTA_PACKAGE_DATA_CACHE).evict(key); } } diff --git a/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java index 1e4ae53829..c2c3bc34a8 100644 --- a/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java +++ b/common/cache/src/main/java/org/thingsboard/server/cache/ota/RedisOtaPackageDataCache.java @@ -22,6 +22,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.stereotype.Service; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_DATA_CACHE; @Service @ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") @@ -63,6 +64,6 @@ public class RedisOtaPackageDataCache implements OtaPackageDataCache { } private byte[] toOtaPackageCacheKey(String key) { - return String.format("%s::%s", OTA_PACKAGE_CACHE, key).getBytes(); + return String.format("%s::%s", OTA_PACKAGE_DATA_CACHE, key).getBytes(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java index 3cc3f56737..ced7a64a0f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/CacheConstants.java @@ -30,4 +30,5 @@ public class CacheConstants { public static final String ATTRIBUTES_CACHE = "attributes"; public static final String TOKEN_OUTDATAGE_TIME_CACHE = "tokensOutdatageTime"; public static final String OTA_PACKAGE_CACHE = "otaPackages"; + public static final String OTA_PACKAGE_DATA_CACHE = "otaPackagesData"; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index ae19576861..536c79843a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -221,8 +221,6 @@ public class BaseOtaPackageService implements OtaPackageService { @Override protected void validateUpdate(TenantId tenantId, OtaPackageInfo otaPackage) { OtaPackageInfo otaPackageOld = otaPackageInfoDao.findById(tenantId, otaPackage.getUuidId()); - - validateUpdateDeviceProfile(otaPackage, otaPackageOld); BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); } }; @@ -261,7 +259,6 @@ public class BaseOtaPackageService implements OtaPackageService { protected void validateUpdate(TenantId tenantId, OtaPackage otaPackage) { OtaPackage otaPackageOld = otaPackageDao.findById(tenantId, otaPackage.getUuidId()); - validateUpdateDeviceProfile(otaPackage, otaPackageOld); BaseOtaPackageService.validateUpdate(otaPackage, otaPackageOld); if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { @@ -270,14 +267,6 @@ public class BaseOtaPackageService implements OtaPackageService { } }; - private void validateUpdateDeviceProfile(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { - if (otaPackageOld.getDeviceProfileId() != null && !otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { - if (otaPackageInfoDao.isOtaPackageUsed(otaPackageOld.getId(), otaPackage.getType(), otaPackageOld.getDeviceProfileId())) { - throw new DataValidationException("Can`t update deviceProfileId because otaPackage is already in use!"); - } - } - } - private static void validateUpdate(OtaPackageInfo otaPackage, OtaPackageInfo otaPackageOld) { if (!otaPackageOld.getType().equals(otaPackage.getType())) { throw new DataValidationException("Updating type is prohibited!"); @@ -291,6 +280,10 @@ public class BaseOtaPackageService implements OtaPackageService { throw new DataValidationException("Updating otaPackage version is prohibited!"); } + if (!otaPackageOld.getDeviceProfileId().equals(otaPackage.getDeviceProfileId())) { + throw new DataValidationException("Updating otaPackage deviceProfile is prohibited!"); + } + if (otaPackageOld.getFileName() != null && !otaPackageOld.getFileName().equals(otaPackage.getFileName())) { throw new DataValidationException("Updating otaPackage file name is prohibited!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index e7cee8c878..ab895e1052 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -408,7 +408,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { } @Test - public void testUpdateDeviceProfileIdWithReferenceByDevice() { + public void testUpdateDeviceProfileId() { OtaPackage firmware = new OtaPackage(); firmware.setTenantId(tenantId); firmware.setDeviceProfileId(deviceProfileId); @@ -422,20 +422,12 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setData(DATA); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - Device device = new Device(); - device.setTenantId(tenantId); - device.setName("My device"); - device.setDeviceProfileId(deviceProfileId); - device.setFirmwareId(savedFirmware.getId()); - Device savedDevice = deviceService.saveDevice(device); - try { thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); + thrown.expectMessage("Updating otaPackage deviceProfile is prohibited!"); savedFirmware.setDeviceProfileId(null); otaPackageService.saveOtaPackage(savedFirmware); } finally { - deviceService.deleteDevice(tenantId, savedDevice.getId()); otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } } @@ -471,38 +463,6 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { } } - @Test - public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() { - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); - DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); - - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(savedDeviceProfile.getId()); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); - - savedDeviceProfile.setFirmwareId(savedFirmware.getId()); - deviceProfileService.saveDeviceProfile(savedDeviceProfile); - - try { - thrown.expect(DataValidationException.class); - thrown.expectMessage("Can`t update deviceProfileId because otaPackage is already in use!"); - savedFirmware.setDeviceProfileId(null); - otaPackageService.saveOtaPackage(savedFirmware); - } finally { - deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); - otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); - } - } - @Test public void testFindFirmwareById() { OtaPackage firmware = new OtaPackage(); diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index 74eb4f43f0..d7a960471b 100644 --- a/dao/src/test/resources/application-test.properties +++ b/dao/src/test/resources/application-test.properties @@ -39,6 +39,9 @@ caffeine.specs.deviceProfiles.maxSize=100000 caffeine.specs.otaPackages.timeToLiveInMinutes=1440 caffeine.specs.otaPackages.maxSize=100000 +caffeine.specs.otaPackagesData.timeToLiveInMinutes=1440 +caffeine.specs.otaPackagesData.maxSize=100000 + caffeine.specs.edges.timeToLiveInMinutes=1440 caffeine.specs.edges.maxSize=100000 From 950f739fcb95425793d9da342f1e2d86f9292068 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 12:43:54 +0300 Subject: [PATCH 25/75] UI: Fix update validate component device-credentials after enable component --- .../home/components/device/device-credentials.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts index 19e9419829..21c8d40a42 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts @@ -148,6 +148,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, } else { this.deviceCredentialsFormGroup.enable({emitEvent: false}); this.updateValidators(); + this.deviceCredentialsFormGroup.updateValueAndValidity(); } } From 98801038fdd054f3951487035d933b26626b15a4 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 11:42:56 +0300 Subject: [PATCH 26/75] UI: Rename 'Add credential' to 'Add credentials' --- .../home/components/wizard/device-wizard-dialog.component.html | 2 +- ui-ngx/src/assets/locale/locale.constant-cs_CZ.json | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- ui-ngx/src/assets/locale/locale.constant-es_ES.json | 2 +- ui-ngx/src/assets/locale/locale.constant-ko_KR.json | 2 +- ui-ngx/src/assets/locale/locale.constant-sl_SI.json | 2 +- ui-ngx/src/assets/locale/locale.constant-zh_CN.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 245cb6503b..39be34b823 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -155,7 +155,7 @@ {{ 'device.credentials' | translate }}
- {{ 'device.wizard.add-credential' | translate }} + {{ 'device.wizard.add-credentials' | translate }} diff --git a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json index 92941c9404..128e8d6d93 100644 --- a/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json +++ b/ui-ngx/src/assets/locale/locale.constant-cs_CZ.json @@ -923,7 +923,7 @@ "existing-device-profile": "Vybrat existující profil zařízení", "specific-configuration": "Specifická konfigurace", "customer-to-assign-device": "Přiřadit zařízení zákazníkovi", - "add-credential": "Přidat přístupový údaj" + "add-credentials": "Přidat přístupový údaj" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6966888ea3..49a29a98b4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1000,7 +1000,7 @@ "existing-device-profile": "Select existing device profile", "specific-configuration": "Specific configuration", "customer-to-assign-device": "Customer to assign the device", - "add-credential": "Add credential" + "add-credentials": "Add credentials" }, "unassign-devices-from-edge-title": "Are you sure you want to unassign { count, plural, 1 {1 device} other {# devices} }?", "unassign-devices-from-edge-text": "After the confirmation all selected devices will be unassigned and won't be accessible by the edge." diff --git a/ui-ngx/src/assets/locale/locale.constant-es_ES.json b/ui-ngx/src/assets/locale/locale.constant-es_ES.json index 9d18102a43..fc1049ca91 100644 --- a/ui-ngx/src/assets/locale/locale.constant-es_ES.json +++ b/ui-ngx/src/assets/locale/locale.constant-es_ES.json @@ -947,7 +947,7 @@ "existing-device-profile": "Seleccionar un perfil existente", "specific-configuration": "Configuración específica", "customer-to-assign-device": "Cliente al que asignar el dispositivo", - "add-credential": "Añadir credencial" + "add-credentials": "Añadir credencial" }, "assign-device-to-edge-text": "Seleccione los dispositivos para asignar al borde", "unassign-device-from-edge-title": "¿Está seguro de que desea desasignar el dispositivo '{{deviceName}}'?", diff --git a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json index cbe962bc2a..f966203fbf 100644 --- a/ui-ngx/src/assets/locale/locale.constant-ko_KR.json +++ b/ui-ngx/src/assets/locale/locale.constant-ko_KR.json @@ -920,7 +920,7 @@ "existing-device-profile": "기존 장치 프로파일 선택", "specific-configuration": "특수 설정", "customer-to-assign-device": "장치에 할당할 커스터머", - "add-credential": "크리덴셜 추가" + "add-credentials": "크리덴셜 추가" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json index 7dd60aaa2f..e57ee78e87 100644 --- a/ui-ngx/src/assets/locale/locale.constant-sl_SI.json +++ b/ui-ngx/src/assets/locale/locale.constant-sl_SI.json @@ -920,7 +920,7 @@ "existing-device-profile": "Select existing device profile", "specific-configuration": "Specific configuration", "customer-to-assign-device": "Customer to assign the device", - "add-credential": "Add credential" + "add-credentials": "Add credentials" } }, "device-profile": { diff --git a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json index b7a3078c3b..99b0864d36 100644 --- a/ui-ngx/src/assets/locale/locale.constant-zh_CN.json +++ b/ui-ngx/src/assets/locale/locale.constant-zh_CN.json @@ -1079,7 +1079,7 @@ "view-credentials": "查看凭据", "view-devices": "查看设备", "wizard": { - "add-credential": "添加凭据", + "add-credentials": "添加凭据", "customer-to-assign-device": "客户分配设备", "device-details": "设备详细信息", "device-wizard": "设备向导", From ea574aa4d867e255253650d507d23c912a1b81fb Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 12:43:54 +0300 Subject: [PATCH 27/75] UI: Fix update validate component device-credentials after enable component --- .../home/components/device/device-credentials.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts index 19e9419829..21c8d40a42 100644 --- a/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts +++ b/ui-ngx/src/app/modules/home/components/device/device-credentials.component.ts @@ -148,6 +148,7 @@ export class DeviceCredentialsComponent implements ControlValueAccessor, OnInit, } else { this.deviceCredentialsFormGroup.enable({emitEvent: false}); this.updateValidators(); + this.deviceCredentialsFormGroup.updateValueAndValidity(); } } From 6f85dc2d85913f7239c1e1c597519571baf73d92 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 14:01:32 +0300 Subject: [PATCH 28/75] UI: Fixed convert string to number in time-series data --- ui-ngx/src/app/core/api/data-aggregator.ts | 28 ++++++------------- .../app/core/api/entity-data-subscription.ts | 11 ++------ 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/ui-ngx/src/app/core/api/data-aggregator.ts b/ui-ngx/src/app/core/api/data-aggregator.ts index 02c1c8649a..971f037282 100644 --- a/ui-ngx/src/app/core/api/data-aggregator.ts +++ b/ui-ngx/src/app/core/api/data-aggregator.ts @@ -16,16 +16,17 @@ import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; import { - AggregationType, calculateIntervalComparisonEndTime, - calculateIntervalEndTime, calculateIntervalStartEndTime, + AggregationType, + calculateIntervalComparisonEndTime, + calculateIntervalEndTime, + calculateIntervalStartEndTime, getCurrentTime, - getCurrentTimeForComparison, getTime, + getTime, SubscriptionTimewindow } from '@shared/models/time/time.models'; import { UtilsService } from '@core/services/utils.service'; -import { deepClone } from '@core/utils'; +import { deepClone, isNumeric } from '@core/utils'; import Timeout = NodeJS.Timeout; -import * as moment_ from 'moment'; export declare type onAggregatedData = (data: SubscriptionData, detectChanges: boolean) => void; @@ -407,24 +408,11 @@ export class DataAggregator { } } - private isNumeric(val: any): boolean { - return (val - parseFloat( val ) + 1) >= 0; - } - private convertValue(val: string): any { - if (!this.noAggregation || val && this.isNumeric(val)) { + if (!this.noAggregation || val && isNumeric(val) && Number(val).toString() === val) { return Number(val); - } else { - return val; - } - } - - private getCurrentTime() { - if (this.subsTw.timeForComparison) { - return getCurrentTimeForComparison(this.subsTw.timeForComparison as moment_.unitOfTime.DurationConstructor, this.subsTw.timezone); - } else { - return getCurrentTime(this.subsTw.timezone); } + return val; } } diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index 9c769f3777..2e78139ce9 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -38,7 +38,7 @@ import { } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; import { EntityDataListener, EntityDataLoadResult } from '@core/api/entity-data.service'; -import { deepClone, isDefined, isDefinedAndNotNull, isObject, objectHashCode } from '@core/utils'; +import { deepClone, isDefined, isDefinedAndNotNull, isNumeric, isObject, objectHashCode } from '@core/utils'; import { PageData } from '@shared/models/page/page-data'; import { DataAggregator } from '@core/api/data-aggregator'; import { NULL_UUID } from '@shared/models/id/has-uuid'; @@ -743,16 +743,11 @@ export class EntityDataSubscription { } } - private isNumeric(val: any): boolean { - return (val - parseFloat( val ) + 1) >= 0; - } - private convertValue(val: string): any { - if (val && this.isNumeric(val) && Number(val).toString() === val) { + if (val && isNumeric(val) && Number(val).toString() === val) { return Number(val); - } else { - return val; } + return val; } private toSubscriptionData(sourceData: {[key: string]: TsValue | TsValue[]}, isTs: boolean): SubscriptionData { From fa3bbb2851a443d088ceb86e08f1daa41111e54a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Wed, 2 Jun 2021 19:03:31 +0300 Subject: [PATCH 29/75] UI: And confirm dialog after change ota package in device profile --- .../app/core/http/device-profile.service.ts | 58 +++++++++++++++---- .../src/app/core/http/ota-package.service.ts | 4 ++ .../entity/entity-details-panel.component.ts | 2 +- ...device-profile-autocomplete.component.html | 2 +- .../device-profile-dialog.component.ts | 2 +- .../entity/entities-table-config.models.ts | 4 +- .../device-profiles-table-config.resolver.ts | 3 +- .../ota-update/ota-update.component.html | 1 + .../assets/locale/locale.constant-en_US.json | 4 +- 9 files changed, 62 insertions(+), 18 deletions(-) diff --git a/ui-ngx/src/app/core/http/device-profile.service.ts b/ui-ngx/src/app/core/http/device-profile.service.ts index 7e3183255a..f5c5625ba2 100644 --- a/ui-ngx/src/app/core/http/device-profile.service.ts +++ b/ui-ngx/src/app/core/http/device-profile.service.ts @@ -14,16 +14,21 @@ /// limitations under the License. /// -import {Injectable} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {PageLink} from '@shared/models/page/page-link'; -import {defaultHttpOptionsFromConfig, RequestConfig} from './http-utils'; -import {Observable} from 'rxjs'; -import {PageData} from '@shared/models/page/page-data'; -import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models'; -import {isDefinedAndNotNull, isEmptyStr} from '@core/utils'; -import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; -import {SortOrder} from '@shared/models/page/sort-order'; +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { PageLink } from '@shared/models/page/page-link'; +import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; +import { forkJoin, Observable, of, throwError } from 'rxjs'; +import { PageData } from '@shared/models/page/page-data'; +import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; +import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; +import { SortOrder } from '@shared/models/page/sort-order'; +import { OtaPackageService } from '@core/http/ota-package.service'; +import { OtaUpdateType } from '@shared/models/ota-package.models'; +import { mergeMap } from 'rxjs/operators'; +import { DialogService } from '@core/services/dialog.service'; +import { TranslateService } from '@ngx-translate/core'; @Injectable({ providedIn: 'root' @@ -31,7 +36,10 @@ import {SortOrder} from '@shared/models/page/sort-order'; export class DeviceProfileService { constructor( - private http: HttpClient + private http: HttpClient, + private otaPackageService: OtaPackageService, + private dialogService: DialogService, + private translate: TranslateService ) { } @@ -70,6 +78,34 @@ export class DeviceProfileService { ); } + public saveDeviceProfileAndConfirmOtaChange(originDeviceProfile: DeviceProfile, deviceProfile: DeviceProfile, + config?: RequestConfig): Observable { + const tasks: Observable[] = []; + if (originDeviceProfile?.id?.id && originDeviceProfile.firmwareId?.id !== deviceProfile.firmwareId?.id) { + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.FIRMWARE, deviceProfile.id.id)); + } else { + tasks.push(of(0)); + } + if (originDeviceProfile?.id?.id && originDeviceProfile.softwareId?.id !== deviceProfile.softwareId?.id) { + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.SOFTWARE, deviceProfile.id.id)); + } else { + tasks.push(of(0)); + } + return forkJoin(tasks).pipe( + mergeMap(([deviceFirmwareUpdate, deviceSoftwareUpdate]) => { + let text = ''; + if (deviceFirmwareUpdate > 0) { + text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate}); + } + if (deviceSoftwareUpdate > 0) { + text += text.length ? '
' : ''; + text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate}); + } + return text !== '' ? this.dialogService.confirm('', text) : of(true); + }), + mergeMap((update) => update ? this.saveDeviceProfile(deviceProfile, config) : throwError('Canceled saving device profiles'))); + } + public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable { return this.http.post('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 34993e5318..3ae1490a88 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -120,4 +120,8 @@ export class OtaPackageService { return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); } + public countUpdateDeviceAfterChangePackage(type: OtaUpdateType, deviceProfileId: string, config?: RequestConfig): Observable { + return this.http.get(`/api/devices/count/${type}?deviceProfileId=${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts index cba32ddcaf..d6378620f9 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entity-details-panel.component.ts @@ -280,7 +280,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV editingEntity.additionalInfo = mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo); } - this.entitiesTableConfig.saveEntity(editingEntity).subscribe( + this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe( (entity) => { this.entity = entity; this.entityComponent.entity = entity; diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html index bd6f92b175..20f271d660 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-autocomplete.component.html @@ -66,5 +66,5 @@ {{ 'device-profile.device-profile-required' | translate }} - {{ hint | translate }} + {{ hint | translate }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts index 0efcad9608..58433f01c4 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile-dialog.component.ts @@ -90,7 +90,7 @@ export class DeviceProfileDialogComponent extends this.submitted = true; if (this.deviceProfileComponent.entityForm.valid) { this.deviceProfile = {...this.deviceProfile, ...this.deviceProfileComponent.entityFormValue()}; - this.deviceProfileService.saveDeviceProfile(this.deviceProfile).subscribe( + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(this.deviceProfile, this.deviceProfile).subscribe( (deviceProfile) => { this.dialogRef.close(deviceProfile); } diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index e6de5dbf84..4b057d8fef 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -37,7 +37,7 @@ export type EntityStringFunction> = (entity: T) => str export type EntityVoidFunction> = (entity: T) => void; export type EntityIdsVoidFunction> = (ids: HasUUID[]) => void; export type EntityCountStringFunction = (count: number) => string; -export type EntityTwoWayOperation> = (entity: T) => Observable; +export type EntityTwoWayOperation> = (entity: T, originalEntity?: T) => Observable; export type EntityByIdOperation> = (id: HasUUID) => Observable; export type EntityIdOneWayOperation = (id: HasUUID) => Observable; export type EntityActionFunction> = (action: EntityAction) => boolean; @@ -173,7 +173,7 @@ export class EntityTableConfig, P extends PageLink = P deleteEntitiesTitle: EntityCountStringFunction = () => ''; deleteEntitiesContent: EntityCountStringFunction = () => ''; loadEntity: EntityByIdOperation = () => of(); - saveEntity: EntityTwoWayOperation = (entity) => of(entity); + saveEntity: EntityTwoWayOperation = (entity, originalEntity) => of(entity); deleteEntity: EntityIdOneWayOperation = () => of(); entitiesFetchFunction: EntitiesFetchFunction = () => of(emptyPageData()); onEntityAction: EntityActionFunction = () => false; diff --git a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts index 49a256dd55..98281c1f88 100644 --- a/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts +++ b/ui-ngx/src/app/modules/home/pages/device-profile/device-profiles-table-config.resolver.ts @@ -104,7 +104,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve this.deviceProfileService.getDeviceProfiles(pageLink); this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id); - this.config.saveEntity = deviceProfile => this.deviceProfileService.saveDeviceProfile(deviceProfile); + this.config.saveEntity = (deviceProfile, originDeviceProfile) => + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(originDeviceProfile, deviceProfile); this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id); this.config.onEntityAction = action => this.onDeviceProfileAction(action); this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index 77a68f72e5..8ea3a19801 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -69,6 +69,7 @@ Date: Thu, 3 Jun 2021 08:21:23 +0300 Subject: [PATCH 30/75] LWM2M: fix bug RPC device of line --- .../device/DeviceActorMessageProcessor.java | 10 +- common/queue/src/main/proto/queue.proto | 2 + .../AbstractLwm2mTransportResource.java | 92 +++++++++++ .../DefaultLwM2MTransportMsgHandler.java | 38 +++-- .../server/DefaultLwM2mTransportService.java | 20 ++- .../server/LwM2mTransportCoapResource.java | 145 ++++++++++++++++++ .../lwm2m/server/LwM2mTransportUtil.java | 8 +- .../server/client/LwM2mClientContextImpl.java | 37 ++++- .../mqtt/session/GatewaySessionHandler.java | 6 +- .../service/DefaultTransportService.java | 1 + 10 files changed, 326 insertions(+), 33 deletions(-) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 5af11d87f5..0c5fec537e 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -192,6 +192,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { syncSessionSet.add(key); } }); + log.warn("46) Rpc syncSessionSet [{}] subscription after sent [{}]",syncSessionSet, rpcSubscriptions); syncSessionSet.forEach(rpcSubscriptions::remove); } @@ -240,6 +241,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { log.debug("[{}] Pushing {} pending RPC messages to new async session [{}]", deviceId, toDeviceRpcPendingMap.size(), sessionId); if (sessionType == SessionType.SYNC) { log.debug("[{}] Cleanup sync rpc session [{}]", deviceId, sessionId); + log.warn("47) Rpc sessionId [{}] Cleanup sync rpc session subscription before [{}]",sessionId, rpcSubscriptions); rpcSubscriptions.remove(sessionId); } } else { @@ -454,7 +456,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } else { SessionInfoMetaData sessionMD = sessions.get(sessionId); if (sessionMD == null) { - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); } sessionMD.setSubscribedToAttributes(true); log.debug("[{}] Registering attributes subscription for session [{}]", deviceId, sessionId); @@ -471,15 +473,17 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { UUID sessionId = getSessionId(sessionInfo); if (subscribeCmd.getUnsubscribe()) { log.debug("[{}] Canceling rpc subscription for session [{}]", deviceId, sessionId); + log.warn("48) Rpc sessionId [{}] Canceling rpc subscription for session subscription before [{}]",sessionId, rpcSubscriptions); rpcSubscriptions.remove(sessionId); } else { SessionInfoMetaData sessionMD = sessions.get(sessionId); if (sessionMD == null) { - sessionMD = new SessionInfoMetaData(new SessionInfo(SessionType.SYNC, sessionInfo.getNodeId())); + sessionMD = new SessionInfoMetaData(new SessionInfo(subscribeCmd.getSessionType(), sessionInfo.getNodeId())); } sessionMD.setSubscribedToRPC(true); log.debug("[{}] Registering rpc subscription for session [{}]", deviceId, sessionId); rpcSubscriptions.put(sessionId, sessionMD.getSessionInfo()); + log.warn("45) sessionId [{}] Registering rpc subscription for session [{}]",sessionId, rpcSubscriptions); sendPendingRequests(context, sessionId, sessionInfo); dumpSessions(); } @@ -509,6 +513,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { log.debug("[{}] Canceling subscriptions for closed session [{}]", deviceId, sessionId); sessions.remove(sessionId); attributeSubscriptions.remove(sessionId); + log.warn("49) Rpc sessionId [{}] Canceling subscriptions for closed session subscription before [{}]",sessionId, rpcSubscriptions); rpcSubscriptions.remove(sessionId); if (sessions.isEmpty()) { reportSessionClose(); @@ -764,6 +769,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { Map sessionsToRemove = sessions.entrySet().stream().filter(kv -> kv.getValue().getLastActivityTime() < expTime).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); sessionsToRemove.forEach((sessionId, sessionMD) -> { sessions.remove(sessionId); + log.warn("50) Rpc sessionId [{}] checkSessionsTimeout subscription before [{}]",sessionId, rpcSubscriptions); rpcSubscriptions.remove(sessionId); attributeSubscriptions.remove(sessionId); notifyTransportAboutClosedSession(sessionId, sessionMD, "session timeout!"); diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index faf88c5620..acea8d6382 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -310,10 +310,12 @@ message SessionCloseNotificationProto { message SubscribeToAttributeUpdatesMsg { bool unsubscribe = 1; + SessionType sessionType = 2; } message SubscribeToRPCMsg { bool unsubscribe = 1; + SessionType sessionType = 2; } message ToDeviceRpcRequestMsg { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java new file mode 100644 index 0000000000..07821abc0c --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java @@ -0,0 +1,92 @@ +/** + * Copyright © 2016-2021 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.transport.lwm2m.server; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Response; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.leshan.core.californium.LwM2mCoapResource; +import org.thingsboard.server.common.transport.TransportServiceCallback; + +@Slf4j +public abstract class AbstractLwm2mTransportResource extends LwM2mCoapResource { + protected final LwM2mTransportMsgHandler handler; + + public AbstractLwm2mTransportResource(LwM2mTransportMsgHandler handler, String name) { + super(name); + this.handler = handler; + } + + @Override + public void handleGET(CoapExchange exchange) { + processHandleGet(exchange); + } + + @Override + public void handlePOST(CoapExchange exchange) { + processHandlePost(exchange); + } + + protected abstract void processHandleGet(CoapExchange exchange); + + protected abstract void processHandlePost(CoapExchange exchange); + + public static class CoapOkCallback implements TransportServiceCallback { + private final CoapExchange exchange; + private final CoAP.ResponseCode onSuccessResponse; + private final CoAP.ResponseCode onFailureResponse; + + public CoapOkCallback(CoapExchange exchange, CoAP.ResponseCode onSuccessResponse, CoAP.ResponseCode onFailureResponse) { + this.exchange = exchange; + this.onSuccessResponse = onSuccessResponse; + this.onFailureResponse = onFailureResponse; + } + + @Override + public void onSuccess(Void msg) { + Response response = new Response(onSuccessResponse); + response.setAcknowledged(isConRequest()); + exchange.respond(response); + } + + @Override + public void onError(Throwable e) { + exchange.respond(onFailureResponse); + } + + private boolean isConRequest() { + return exchange.advanced().getRequest().isConfirmable(); + } + } + + public static class CoapNoOpCallback implements TransportServiceCallback { + private final CoapExchange exchange; + + CoapNoOpCallback(CoapExchange exchange) { + this.exchange = exchange; + } + + @Override + public void onSuccess(Void msg) { + } + + @Override + public void onError(Throwable e) { + exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); + } + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index e0a85bc77d..fecd149230 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -42,10 +42,10 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; -import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -87,9 +87,9 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; @@ -189,13 +189,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); - TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() + log.warn("40) sessionId [{}] Registering rpc subscription after Registration client",new UUID (sessionInfo.getSessionIdMSB(),sessionInfo.getSessionIdLSB())); + transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build()) - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) - .build(); - transportService.process(msg, null); + .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build()) + .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build()) + .build(), null); this.getInfoFirmwareUpdate(lwM2MClient, null); this.getInfoSoftwareUpdate(lwM2MClient, null); this.initLwM2mFromClientValue(registration, lwM2MClient); @@ -250,7 +252,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler unRegistrationExecutor.submit(() -> { try { this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); - this.closeClientSession(registration); +// this.closeClientSession(registration); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId()); @@ -262,10 +264,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); if (sessionInfo != null) { transportService.deregisterSession(sessionInfo); + // TO DO may problem, better by registrationId sessionStore.remove(registration.getEndpoint()); this.doCloseSession(sessionInfo); clientContext.removeClientByRegistrationId(registration.getId()); - log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + log.warn("52) Client close session [{}]: [{}] unReg [{}] name [{}] profile ", new UUID (sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()), registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -446,10 +449,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { // #1 this.checkRpcRequestTimeout(); - log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); + log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, requestUUID); String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); - UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); if (!this.rpcSubscriptions.containsKey(requestUUID)) { this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; @@ -479,8 +482,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } private void checkRpcRequestTimeout() { - Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); - rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); + log.warn ("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); + if (rpcSubscriptions.size() > 0) { + Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); + log.warn ("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); + log.warn ("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); + rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); + } + log.warn ("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); } public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { @@ -1372,7 +1381,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getFwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); + log.warn("11) OtaPackageIdMSB: [{}] OtaPackageIdLSB: [{}]", response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); + lwM2MClient.getFwUpdate().setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); if (rpcRequest == null) { lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 90e3e13033..688ab88eeb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -28,7 +28,6 @@ import org.eclipse.leshan.server.californium.registration.CaliforniumRegistratio import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.security.EditableSecurityStore; import org.springframework.stereotype.Component; -import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -58,14 +57,14 @@ import java.security.spec.InvalidParameterSpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Arrays; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_COAP_RESOURCE; @Slf4j @Component @@ -81,7 +80,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { private final LwM2mTransportContext context; private final LwM2MTransportServerConfig config; private final LwM2mTransportServerHelper helper; - private final LwM2mTransportMsgHandler handler; + private final DefaultLwM2MTransportMsgHandler handler; private final CaliforniumRegistrationStore registrationStore; private final EditableSecurityStore securityStore; private final LwM2mClientContext lwM2mClientContext; @@ -96,6 +95,19 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { new LWM2MGenerationPSkRPkECC(); } this.server = getLhServer(); + /** + * Add a resource to the server. + * CoapResource -> + * path = FW_PACKAGE or SW_PACKAGE + * nameFile = "BC68JAR01A09_TO_BC68JAR01A10.bin" + * "coap://host:port/{path}/{token}/{nameFile}" + */ + + + LwM2mTransportCoapResource fwCoapResource = new LwM2mTransportCoapResource(handler, FW_COAP_RESOURCE); + LwM2mTransportCoapResource swCoapResource = new LwM2mTransportCoapResource(handler, SW_COAP_RESOURCE); + this.server.coap().getServer().add(fwCoapResource); + this.server.coap().getServer().add(swCoapResource); this.startLhServer(); this.context.setServer(server); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java new file mode 100644 index 0000000000..c03b38451a --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -0,0 +1,145 @@ +/** + * Copyright © 2016-2021 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.transport.lwm2m.server; + +import lombok.extern.slf4j.Slf4j; +import org.eclipse.californium.core.coap.CoAP; +import org.eclipse.californium.core.coap.Request; +import org.eclipse.californium.core.coap.Response; +import org.eclipse.californium.core.network.Exchange; +import org.eclipse.californium.core.observe.ObserveRelation; +import org.eclipse.californium.core.server.resources.CoapExchange; +import org.eclipse.californium.core.server.resources.Resource; +import org.eclipse.californium.core.server.resources.ResourceObserver; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +@Slf4j +public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { + private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); + private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); + + public LwM2mTransportCoapResource(LwM2mTransportMsgHandler handler, String name) { + super(handler, name); + this.setObservable(true); // enable observing + this.addObserver(new CoapResourceObserver()); + } + + + @Override + public void checkObserveRelation(Exchange exchange, Response response) { + String token = getTokenFromRequest(exchange.getRequest()); + final ObserveRelation relation = exchange.getRelation(); + if (relation == null || relation.isCanceled()) { + return; // because request did not try to establish a relation + } + if (CoAP.ResponseCode.isSuccess(response.getCode())) { + + if (!relation.isEstablished()) { + relation.setEstablished(); + addObserveRelation(relation); + } + AtomicInteger notificationCounter = tokenToObserveNotificationSeqMap.computeIfAbsent(token, s -> new AtomicInteger(0)); + response.getOptions().setObserve(notificationCounter.getAndIncrement()); + } // ObserveLayer takes care of the else case + } + + + @Override + protected void processHandleGet(CoapExchange exchange) { + log.warn("1) processHandleGet [{}]", exchange); +// exchange.respond(CoAP.ResponseCode.BAD_REQUEST); +// int ver = 10; + int ver = 9; + UUID currentId; + if (ver == 10) { + long mSB = 4951557297924280811L; + long lSb = -8451242882176289074L; + currentId = new UUID(mSB, lSb); + } else { + long mSB = 9085827945869414891L; + long lSb = -9086716326447629319L; + currentId = new UUID(mSB, lSb); + } + String resource = exchange.getRequestOptions().getUriPath().get(0); + String token = exchange.getRequestOptions().getUriPath().get(1); + exchange.respond(CoAP.ResponseCode.CONTENT, this.getFwData(currentId)); + + } + + @Override + protected void processHandlePost(CoapExchange exchange) { + log.warn("2) processHandleGet [{}]", exchange); + } + + /** + * Override the default behavior so that requests to sub resources (typically /{name}/{token}) are handled by + * /name resource. + */ + @Override + public Resource getChild(String name) { + return this; + } + + + private String getTokenFromRequest(Request request) { + return (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getAddress().getHostAddress() : "null") + + ":" + (request.getSourceContext() != null ? request.getSourceContext().getPeerAddress().getPort() : -1) + ":" + request.getTokenString(); + } + + public class CoapResourceObserver implements ResourceObserver { + + @Override + public void changedName(String old) { + + } + + @Override + public void changedPath(String old) { + + } + + @Override + public void addedChild(Resource child) { + + } + + @Override + public void removedChild(Resource child) { + + } + + @Override + public void addedObserveRelation(ObserveRelation relation) { + + } + + @Override + public void removedObserveRelation(ObserveRelation relation) { + + } + } + + private byte[] getFwData(UUID currentId) { + int chunkSize = 0; + int chunk = 0; + return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString(), chunkSize, chunk); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index adf9e07bc4..2e7681250a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -43,11 +43,11 @@ import org.eclipse.leshan.server.registration.Registration; import org.nustaq.serialization.FSTConfiguration; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.common.data.ota.OtaPackageUtil; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; @@ -77,14 +77,14 @@ import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK; import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; import static org.eclipse.leshan.core.model.ResourceModel.Type.TIME; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERIFIED; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; @Slf4j public class LwM2mTransportUtil { @@ -140,6 +140,7 @@ public class LwM2mTransportUtil { public static final String METHOD_KEY = "methodName"; // Firmware + public static final String FW_COAP_RESOURCE = "fw"; public static final String FW_UPDATE = "Firmware update"; public static final Integer FW_ID = 5; // Package W @@ -156,6 +157,7 @@ public class LwM2mTransportUtil { public static final String FW_UPDATE_ID = "/5/0/2"; // Software + public static final String SW_COAP_RESOURCE = "sw"; public static final String SW_UPDATE = "Software update"; public static final Integer SW_ID = 9; // Package W diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index 8674516e85..a29edf6424 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -37,6 +37,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -68,12 +69,15 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getOrRegister(Registration registration) { + LwM2mClient client = null; if (registration == null) { - return null; + return client; + } + if (lwM2mClientsByRegistrationId.size()>0) { + client = this.lwM2mClientsByRegistrationId.get(registration.getId()); } - LwM2mClient client = lwM2mClientsByRegistrationId.get(registration.getId()); if (client == null) { - client = lwM2mClientsByEndpoint.get(registration.getEndpoint()); + client = this.lwM2mClientsByEndpoint.get(registration.getEndpoint()); if (client == null) { client = registerOrUpdate(registration); } @@ -83,12 +87,29 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getClient(TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> + LwM2mClient lwM2mClient = null; + Predicate isClientFilter = c -> (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) - .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) - - ).findAny().get(); - if (lwM2mClient == null) { + .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))); +// if (this.lwM2mClientsByEndpoint.size()>0) { +// lwM2mClient = this.lwM2mClientsByEndpoint.values().stream().filter(c -> +// (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) +// .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) +// ).findAny().get(); +// } + if (this.lwM2mClientsByEndpoint.size()>0) { + lwM2mClient = this.lwM2mClientsByEndpoint.values().stream().filter(isClientFilter).findAny().get(); + } +// if (lwM2mClient == null && this.lwM2mClientsByRegistrationId.size() > 0) { +// lwM2mClient = this.lwM2mClientsByRegistrationId.values().stream().filter(c -> +// (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) +// .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) +// ).findAny().get(); +// } + if (lwM2mClient == null && this.lwM2mClientsByRegistrationId.size() > 0) { + lwM2mClient = this.lwM2mClientsByRegistrationId.values().stream().filter(isClientFilter).findAny().get(); + } + if (lwM2mClient == null){ log.warn("Device TimeOut? lwM2mClient is null."); log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); log.error("", new RuntimeException()); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java index 7882ad2410..891c466151 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/session/GatewaySessionHandler.java @@ -259,8 +259,10 @@ public class GatewaySessionHandler { transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(deviceSessionInfo) .setSessionEvent(DefaultTransportService.getSessionEventMsg(TransportProtos.SessionEvent.OPEN)) - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build()) - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().build()) + .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build()) + .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder() + .setSessionType(TransportProtos.SessionType.ASYNC).build()) .build(), null); } futureToSet.set(devices.get(deviceName)); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java index 77b75dc07a..79b022c6b4 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/DefaultTransportService.java @@ -708,6 +708,7 @@ public class DefaultTransportService implements TransportService { log.debug("Stopping scheduler to avoid resending response if request has been ack."); currentSession.getScheduledFuture().cancel(false); } + log.warn("54) session [{}] deregisterSession Stopping scheduler to avoid resending response if request has been ack.", toSessionId(sessionInfo)); sessions.remove(toSessionId(sessionInfo)); } From d46967270629578ee5809e92b7de7edc8f81df8c Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 3 Jun 2021 10:44:11 +0300 Subject: [PATCH 31/75] LWM2M: fix bug test --- .../DefaultLwM2MTransportMsgHandler.java | 54 +++++++++---------- .../server/LwM2mTransportCoapResource.java | 1 + 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index fecd149230..e3c4fd4646 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -189,7 +189,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); - log.warn("40) sessionId [{}] Registering rpc subscription after Registration client",new UUID (sessionInfo.getSessionIdMSB(),sessionInfo.getSessionIdLSB())); + log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); transportService.process(TransportProtos.TransportToDeviceActorMsg.newBuilder() .setSessionInfo(sessionInfo) .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) @@ -252,7 +252,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler unRegistrationExecutor.submit(() -> { try { this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); -// this.closeClientSession(registration); + this.removeClientCash(registration); } catch (Throwable t) { log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId()); @@ -260,15 +260,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler }); } - private void closeClientSession(Registration registration) { + private void removeClientCash(Registration registration) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); if (sessionInfo != null) { - transportService.deregisterSession(sessionInfo); - // TO DO may problem, better by registrationId +// transportService.deregisterSession(sessionInfo); +// this.doCloseSession(sessionInfo); sessionStore.remove(registration.getEndpoint()); - this.doCloseSession(sessionInfo); + clientContext.removeClientByRegistrationId(registration.getId()); - log.warn("52) Client close session [{}]: [{}] unReg [{}] name [{}] profile ", new UUID (sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()), registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); + log.warn("52) Client close session [{}]: [{}] unReg [{}] name [{}] profile ", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()), registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -355,7 +355,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { - log.warn ("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); + log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); @@ -399,9 +399,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } }); log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); - } - else if (lwM2MClient == null) { - log.error ("OnAttributeUpdate, lwM2MClient is null"); + } else if (lwM2MClient == null) { + log.error("OnAttributeUpdate, lwM2MClient is null"); } } @@ -450,7 +449,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler // #1 this.checkRpcRequestTimeout(); UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); - log.warn ("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, requestUUID); + log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, requestUUID); String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); if (!this.rpcSubscriptions.containsKey(requestUUID)) { @@ -482,14 +481,14 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } private void checkRpcRequestTimeout() { - log.warn ("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); + log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); if (rpcSubscriptions.size() > 0) { Set rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); - log.warn ("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); - log.warn ("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); + log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); + log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); } - log.warn ("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); + log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); } public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { @@ -515,7 +514,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler @Override public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { - log.warn ("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); + log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); transportService.process(sessionInfo, toDeviceResponse, null); } @@ -901,9 +900,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); - } - - else { + } else { return null; } } @@ -1284,8 +1281,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), getValueFromKvProto(tsKvProto.getKv()), pathIdVer); }); - } - else { + } else { log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); } } @@ -1299,7 +1295,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler SessionInfoProto sessionInfoProto = lwM2MClient.getSession(); if (sessionInfoProto == null) { log.info("[{}] [{}]", lwM2MClient.getEndpoint(), CLIENT_NOT_AUTHORIZED); - this.closeClientSession(lwM2MClient.getRegistration()); + this.removeClientCash(lwM2MClient.getRegistration()); } return sessionInfoProto; } @@ -1377,7 +1373,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { - log.warn ("7) firmware start with ver: [{}]", response.getVersion()); + log.warn("7) firmware start with ver: [{}]", response.getVersion()); lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); @@ -1385,9 +1381,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler lwM2MClient.getFwUpdate().setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); if (rpcRequest == null) { lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); - } - else { - lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + } else { + lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); } } else { log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); @@ -1421,9 +1416,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); if (rpcRequest == null) { lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); - } - else { - lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + } else { + lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); } } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index c03b38451a..d2449c4634 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -79,6 +79,7 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { } String resource = exchange.getRequestOptions().getUriPath().get(0); String token = exchange.getRequestOptions().getUriPath().get(1); + exchange.respond(CoAP.ResponseCode.CONTENT, this.getFwData(currentId)); } From f9713c4fce9f67a8ef0971970e480ad7347536b4 Mon Sep 17 00:00:00 2001 From: AndrewVolosytnykhThingsboard <77969531+AndrewVolosytnykhThingsboard@users.noreply.github.com> Date: Thu, 3 Jun 2021 13:53:37 +0300 Subject: [PATCH 32/75] Rest client resources and firmware methods (#4522) * Added methods to RestClient from Resources and Firmware Controllers * Improvements according to last master * Refactored for new entity names, added new method from device profile controller --- .../thingsboard/rest/client/RestClient.java | 203 +++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 433e2f8d96..8ad95805a5 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -19,18 +19,25 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.springframework.core.ParameterizedTypeReference; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.support.HttpRequestWrapper; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.rest.client.utils.RestJsonConverter; import org.thingsboard.server.common.data.AdminSettings; @@ -48,6 +55,10 @@ import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.EntityViewInfo; import org.thingsboard.server.common.data.Event; +import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.TenantInfo; import org.thingsboard.server.common.data.TenantProfile; @@ -78,8 +89,10 @@ import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleNodeId; +import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantProfileId; import org.thingsboard.server.common.data.id.UserId; @@ -91,6 +104,8 @@ import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; @@ -127,9 +142,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.stream.Collectors; @@ -147,7 +162,6 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { private final ObjectMapper objectMapper = new ObjectMapper(); private ExecutorService service = ThingsBoardExecutors.newWorkStealingPool(10, getClass()); - protected static final String ACTIVATE_TOKEN_REGEX = "/api/noauth/activate?activateToken="; public RestClient(String baseURL) { @@ -1238,6 +1252,21 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { HttpEntity.EMPTY, Device.class, tenantId, deviceId).getBody(); } + public Long countDevicesByTenantIdAndDeviceProfileIdAndEmptyOtaPackage(OtaPackageType otaPackageType, DeviceProfileId deviceProfileId) { + Map params = new HashMap<>(); + params.put("otaPackageType", otaPackageType.name()); + params.put("deviceProfileId", deviceProfileId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/devices/count/{otaPackageType}?deviceProfileId={deviceProfileId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() { + }, + params + ).getBody(); + } + @Deprecated public Device createDevice(String name, String type) { Device device = new Device(); @@ -2830,6 +2859,176 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { restTemplate.postForEntity(baseURL + "/api/edge/sync/{edgeId}", null, EdgeId.class, params); } + public ResponseEntity downloadResource(TbResourceId resourceId) { + Map params = new HashMap<>(); + params.put("resourceId", resourceId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/resource/{resourceId}/download", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference<>() {}, + params + ); + } + + public TbResourceInfo getResourceInfoById(TbResourceId resourceId) { + Map params = new HashMap<>(); + params.put("resourceId", resourceId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/resource/info/{resourceId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() {}, + params + ).getBody(); + } + + public TbResource getResourceId(TbResourceId resourceId) { + Map params = new HashMap<>(); + params.put("resourceId", resourceId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/resource/{resourceId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() {}, + params + ).getBody(); + } + + public TbResource saveResource(TbResource resource) { + return restTemplate.postForEntity( + baseURL + "/api/resource", + resource, + TbResource.class + ).getBody(); + } + + public PageData getResources(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + return restTemplate.exchange( + baseURL + "/api/resource?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() {}, + params + ).getBody(); + } + + public void deleteResource(TbResourceId resourceId) { + restTemplate.delete("/api/resource/{resourceId}", resourceId.getId().toString()); + } + + public ResponseEntity downloadOtaPackage(OtaPackageId otaPackageId) { + Map params = new HashMap<>(); + params.put("otaPackageId", otaPackageId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/otaPackage/{otaPackageId}/download", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference<>() {}, + params + ); + } + + public OtaPackageInfo getOtaPackageInfoById(OtaPackageId otaPackageId) { + Map params = new HashMap<>(); + params.put("otaPackageId", otaPackageId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/otaPackage/info/{otaPackageId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() {}, + params + ).getBody(); + } + + public OtaPackage getOtaPackageById(OtaPackageId otaPackageId) { + Map params = new HashMap<>(); + params.put("otaPackageId", otaPackageId.getId().toString()); + + return restTemplate.exchange( + baseURL + "/api/otaPackage/{otaPackageId}", + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference() {}, + params + ).getBody(); + } + + public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { + return restTemplate.postForEntity(baseURL + "/api/otaPackage", otaPackageInfo, OtaPackageInfo.class).getBody(); + } + + public OtaPackage saveOtaPackageData(OtaPackageId otaPackageId, String checkSum, ChecksumAlgorithm checksumAlgorithm, MultipartFile file) throws Exception { + HttpHeaders header = new HttpHeaders(); + header.setContentType(MediaType.MULTIPART_FORM_DATA); + + MultiValueMap fileMap = new LinkedMultiValueMap<>(); + fileMap.add(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=" + file.getName()); + HttpEntity fileEntity = new HttpEntity<>(new ByteArrayResource(file.getBytes()), fileMap); + + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", fileEntity); + HttpEntity> requestEntity = new HttpEntity<>(body, header); + + Map params = new HashMap<>(); + params.put("otaPackageId", otaPackageId.getId().toString()); + params.put("checksumAlgorithm", checksumAlgorithm.name()); + String url = "/api/otaPackage/{otaPackageId}?checksumAlgorithm={checksumAlgorithm}"; + + if(checkSum != null) { + url += "&checkSum={checkSum}"; + } + + return restTemplate.postForEntity( + baseURL + url, requestEntity, OtaPackage.class, params + ).getBody(); + } + + public PageData getOtaPackages(PageLink pageLink) { + Map params = new HashMap<>(); + addPageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + "/api/otaPackages?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params + ).getBody(); + } + + public PageData getOtaPackages(DeviceProfileId deviceProfileId, + OtaPackageType otaPackageType, + boolean hasData, + PageLink pageLink) { + Map params = new HashMap<>(); + params.put("hasData", String.valueOf(hasData)); + params.put("deviceProfileId", deviceProfileId.getId().toString()); + params.put("type", otaPackageType.name()); + addPageLinkToParam(params, pageLink); + + return restTemplate.exchange( + baseURL + "/api/otaPackages/{deviceProfileId}/{type}/{hasData}?" + getUrlParams(pageLink), + HttpMethod.GET, + HttpEntity.EMPTY, + new ParameterizedTypeReference>() { + }, + params + ).getBody(); + } + + public void deleteOtaPackage(OtaPackageId otaPackageId) { + restTemplate.delete(baseURL + "/api/otaPackage/{otaPackageId}", otaPackageId.getId().toString()); + } + @Deprecated public Optional getAttributes(String accessToken, String clientKeys, String sharedKeys) { Map params = new HashMap<>(); From c176ca94aa0f612c3bb3856ae2acf3a399cc74a4 Mon Sep 17 00:00:00 2001 From: Kien Truong Date: Thu, 3 Jun 2021 11:05:02 +0700 Subject: [PATCH 33/75] Fix wrong configuration key for Compression Type Fix #4678 --- msa/js-executor/queue/kafkaTemplate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/js-executor/queue/kafkaTemplate.js b/msa/js-executor/queue/kafkaTemplate.js index 2f4fd8751d..bec9a47b44 100644 --- a/msa/js-executor/queue/kafkaTemplate.js +++ b/msa/js-executor/queue/kafkaTemplate.js @@ -24,7 +24,7 @@ const topicProperties = config.get('kafka.topic_properties'); const kafkaClientId = config.get('kafka.client_id'); const acks = Number(config.get('kafka.acks')); const requestTimeout = Number(config.get('kafka.requestTimeout')); -const compressionType = (config.get('kafka.requestTimeout') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; +const compressionType = (config.get('kafka.compression') === "gzip") ? CompressionTypes.GZIP : CompressionTypes.None; let kafkaClient; let kafkaAdmin; From 45fa1a7bd2a3a6bd1b9fd34da8bc8295cf0e531f Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 3 Jun 2021 17:05:21 +0300 Subject: [PATCH 34/75] LWM2M: add response FirmWareUpdate --- .../AbstractLwm2mTransportResource.java | 2 + .../DefaultLwM2MTransportMsgHandler.java | 4 +- .../server/LwM2mTransportCoapResource.java | 44 +++++++++++++++---- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java index 07821abc0c..28c10df718 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java @@ -89,4 +89,6 @@ public abstract class AbstractLwm2mTransportResource extends LwM2mCoapResource { exchange.respond(CoAP.ResponseCode.INTERNAL_SERVER_ERROR); } } + + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index e3c4fd4646..0fe5df844f 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -139,6 +139,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public final LwM2mClientContext clientContext; public final LwM2mTransportRequest lwM2mTransportRequest; private final Map rpcSubscriptions; + private final Map getCoapResource; public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, LwM2mClientContext clientContext, @@ -154,6 +155,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler this.context = context; this.adaptor = adaptor; this.rpcSubscriptions = new ConcurrentHashMap<>(); + this.getCoapResource = new ConcurrentHashMap<>(); this.sessionStore = sessionStore; } @@ -199,7 +201,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler .setSessionType(TransportProtos.SessionType.ASYNC).build()) .build(), null); this.getInfoFirmwareUpdate(lwM2MClient, null); - this.getInfoSoftwareUpdate(lwM2MClient, null); +// this.getInfoSoftwareUpdate(lwM2MClient, null); this.initLwM2mFromClientValue(registration, lwM2MClient); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index d2449c4634..0f2e885274 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -30,6 +30,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_COAP_RESOURCE; + @Slf4j public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); @@ -64,9 +67,9 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { @Override protected void processHandleGet(CoapExchange exchange) { log.warn("1) processHandleGet [{}]", exchange); -// exchange.respond(CoAP.ResponseCode.BAD_REQUEST); -// int ver = 10; - int ver = 9; + // exchange.respond(CoAP.ResponseCode.BAD_REQUEST); + int ver = 10; +// int ver = 9; UUID currentId; if (ver == 10) { long mSB = 4951557297924280811L; @@ -77,10 +80,37 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { long lSb = -9086716326447629319L; currentId = new UUID(mSB, lSb); } - String resource = exchange.getRequestOptions().getUriPath().get(0); + + String coapResource = exchange.getRequestOptions().getUriPath().get(0); String token = exchange.getRequestOptions().getUriPath().get(1); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + int chunk = 0; + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = this.getFwData(currentId); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + boolean moreFlag = fwData.length > chunkSize; + response.getOptions().setBlock2(chunkSize, moreFlag, chunk); + exchange.respond(response); + } + + } +// List options = exchange.advanced().getRequest().getOptions().getUriPath(); +// options.stream() +// .filter(o -> FW_COAP_RESOURCE.equals(o)) +// .findFirst() +// .ifPresent(o -> System.err.println(o.getNumber() + " " + o.getStringValue())); + + if (FW_COAP_RESOURCE.equals(coapResource)) { + + } + else if (SW_COAP_RESOURCE.equals(coapResource)) { + + } + + - exchange.respond(CoAP.ResponseCode.CONTENT, this.getFwData(currentId)); } @@ -138,9 +168,7 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { } private byte[] getFwData(UUID currentId) { - int chunkSize = 0; - int chunk = 0; - return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString(), chunkSize, chunk); + return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); } } From 04e218166cfaebf24ecec46773bdfbd125c20103 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Thu, 3 Jun 2021 17:23:30 +0300 Subject: [PATCH 35/75] Minor Improvements to Alarms Cleanup --- .../service/ttl/alarms/AlarmsCleanUpService.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java index 7d6ebb6939..3b76a6cbca 100644 --- a/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java +++ b/application/src/main/java/org/thingsboard/server/service/ttl/alarms/AlarmsCleanUpService.java @@ -26,6 +26,7 @@ import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.dao.alarm.AlarmDao; @@ -35,6 +36,7 @@ import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.queue.discovery.PartitionService; +import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.action.RuleEngineEntityActionService; import org.thingsboard.server.service.ttl.AbstractCleanUpService; @@ -45,7 +47,7 @@ import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; -@PsqlDao +@TbCoreComponent @Service @Slf4j @RequiredArgsConstructor @@ -64,7 +66,7 @@ public class AlarmsCleanUpService { @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${sql.ttl.alarms.checking_interval})}", fixedDelayString = "${sql.ttl.alarms.checking_interval}") public void cleanUp() { PageLink tenantsBatchRequest = new PageLink(10_000, 0); - PageLink removalBatchRequest = new PageLink(removalBatchSize, 0); + PageLink removalBatchRequest = new PageLink(removalBatchSize, 0 ); PageData tenantsIds; do { tenantsIds = tenantDao.findTenantsIds(tenantsBatchRequest); @@ -79,11 +81,11 @@ public class AlarmsCleanUpService { } long ttl = TimeUnit.DAYS.toMillis(tenantProfileConfiguration.get().getAlarmsTtlDays()); - long outdatageTime = System.currentTimeMillis() - ttl; + long expirationTime = System.currentTimeMillis() - ttl; long totalRemoved = 0; while (true) { - PageData toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(outdatageTime, tenantId, removalBatchRequest); + PageData toRemove = alarmDao.findAlarmsIdsByEndTsBeforeAndTenantId(expirationTime, tenantId, removalBatchRequest); toRemove.getData().forEach(alarmId -> { relationService.deleteEntityRelations(tenantId, alarmId); Alarm alarm = alarmService.deleteAlarm(tenantId, alarmId).getAlarm(); @@ -97,7 +99,7 @@ public class AlarmsCleanUpService { } if (totalRemoved > 0) { - log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(outdatageTime)); + log.info("Removed {} outdated alarm(s) for tenant {} older than {}", totalRemoved, tenantId, new Date(expirationTime)); } } From 43b4f4461d22784ffd19a4fda38523c44dc926c7 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 3 Jun 2021 18:34:39 +0300 Subject: [PATCH 36/75] created data limits for resources and otaPackages, added url for the otaPackage --- .../main/data/upgrade/3.2.2/schema_update.sql | 1 + .../server/actors/ActorSystemContext.java | 10 + .../actors/ruleChain/DefaultTbContext.java | 12 + .../controller/OtaPackageController.java | 9 +- .../ota/DefaultOtaPackageStateService.java | 41 ++- .../resource/DefaultTbResourceService.java | 5 + .../service/resource/TbResourceService.java | 1 + .../transport/DefaultTransportApiService.java | 3 + .../resource/BaseTbResourceServiceTest.java | 65 +++++ .../server/dao/ota/OtaPackageService.java | 4 +- .../server/dao/resource/ResourceService.java | 2 +- .../server/common/data/OtaPackage.java | 6 +- .../server/common/data/OtaPackageInfo.java | 7 + .../server/common/data/ota/OtaPackageKey.java | 2 +- .../DefaultTenantProfileConfiguration.java | 2 + .../server/dao/TenantEntityWithDataDao.java | 23 ++ .../server/dao/model/ModelConstants.java | 1 + .../dao/model/sql/OtaPackageEntity.java | 6 + .../dao/model/sql/OtaPackageInfoEntity.java | 10 +- .../server/dao/ota/BaseOtaPackageService.java | 77 ++++-- .../server/dao/ota/OtaPackageDao.java | 7 +- .../server/dao/ota/OtaPackageInfoDao.java | 2 +- .../dao/resource/BaseResourceService.java | 24 +- .../server/dao/resource/TbResourceDao.java | 3 +- .../server/dao/service/DataValidator.java | 15 ++ .../server/dao/sql/ota/JpaOtaPackageDao.java | 5 + .../dao/sql/ota/JpaOtaPackageInfoDao.java | 3 +- .../dao/sql/ota/OtaPackageInfoRepository.java | 9 +- .../dao/sql/ota/OtaPackageRepository.java | 5 + .../dao/sql/resource/JpaTbResourceDao.java | 4 + .../sql/resource/TbResourceRepository.java | 3 + .../resources/sql/schema-entities-hsql.sql | 1 + .../main/resources/sql/schema-entities.sql | 1 + .../server/dao/SqlDaoServiceTestSuite.java | 8 +- .../dao/service/AbstractServiceTest.java | 5 +- .../service/BaseOtaPackageServiceTest.java | 253 +++++++++--------- .../rule/engine/api/TbContext.java | 6 + .../src/app/core/http/ota-package.service.ts | 2 +- ...enant-profile-configuration.component.html | 24 ++ ...-tenant-profile-configuration.component.ts | 2 + ui-ngx/src/app/shared/models/tenant.model.ts | 5 + .../assets/locale/locale.constant-en_US.json | 6 + 42 files changed, 491 insertions(+), 189 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/TenantEntityWithDataDao.java diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 2814e18c2f..5647c301cf 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS ota_package ( type varchar(32) NOT NULL, title varchar(255) NOT NULL, version varchar(255) NOT NULL, + url varchar(255), file_name varchar(255), content_type varchar(255), checksum_algorithm varchar(32), diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java index c54f940c38..b52f85af92 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java @@ -60,7 +60,9 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.event.EventService; import org.thingsboard.server.dao.nosql.CassandraBufferedRateExecutor; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleNodeStateService; import org.thingsboard.server.dao.tenant.TenantProfileService; @@ -311,6 +313,14 @@ public class ActorSystemContext { @Autowired(required = false) @Getter private EdgeRpcService edgeRpcService; + @Lazy + @Autowired(required = false) + @Getter private ResourceService resourceService; + + @Lazy + @Autowired(required = false) + @Getter private OtaPackageService otaPackageService; + @Value("${actors.session.max_concurrent_sessions_per_device:1}") @Getter private long maxConcurrentSessionsPerDevice; diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java index 1ddb99d7b7..9a1afb9ff8 100644 --- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java +++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java @@ -69,7 +69,9 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -486,6 +488,16 @@ class DefaultTbContext implements TbContext { return mainCtx.getEntityViewService(); } + @Override + public ResourceService getResourceService() { + return mainCtx.getResourceService(); + } + + @Override + public OtaPackageService getOtaPackageService() { + return mainCtx.getOtaPackageService(); + } + @Override public RuleEngineDeviceProfileCache getDeviceProfileCache() { return mainCtx.getDeviceProfileCache(); diff --git a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java index 02e9d4b305..13d39b0b2a 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OtaPackageController.java @@ -64,6 +64,10 @@ public class OtaPackageController extends BaseController { OtaPackageId otaPackageId = new OtaPackageId(toUUID(strOtaPackageId)); OtaPackage otaPackage = checkOtaPackageId(otaPackageId, Operation.READ); + if (otaPackage.hasUrl()) { + return ResponseEntity.badRequest().build(); + } + ByteArrayResource resource = new ByteArrayResource(otaPackage.getData().array()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + otaPackage.getFileName()) @@ -182,11 +186,10 @@ public class OtaPackageController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) + @RequestMapping(value = "/otaPackages/{deviceProfileId}/{type}", method = RequestMethod.GET) @ResponseBody public PageData getOtaPackages(@PathVariable("deviceProfileId") String strDeviceProfileId, @PathVariable("type") String strType, - @PathVariable("hasData") boolean hasData, @RequestParam int pageSize, @RequestParam int page, @RequestParam(required = false) String textSearch, @@ -197,7 +200,7 @@ public class OtaPackageController extends BaseController { try { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); return checkNotNull(otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), - new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), hasData, pageLink)); + new DeviceProfileId(toUUID(strDeviceProfileId)), OtaPackageType.valueOf(strType), pageLink)); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index c5d0c0472f..3bb85f0ffb 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; @@ -65,6 +66,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageKey.SIZE; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageKey.TITLE; import static org.thingsboard.server.common.data.ota.OtaPackageKey.TS; +import static org.thingsboard.server.common.data.ota.OtaPackageKey.URL; import static org.thingsboard.server.common.data.ota.OtaPackageKey.VERSION; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; @@ -261,11 +263,12 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { } - private void update(Device device, OtaPackageInfo firmware, long ts) { + private void update(Device device, OtaPackageInfo otaPackage, long ts) { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); + OtaPackageType otaPackageType = otaPackage.getType(); - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), OtaPackageUpdateStatus.INITIATED.name())); + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(otaPackageType, STATE), OtaPackageUpdateStatus.INITIATED.name())); telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { @Override @@ -280,11 +283,21 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { }); List attributes = new ArrayList<>(); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion()))); - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm().name()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion()))); + if (StringUtils.isEmpty(otaPackage.getUrl())) { + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); + remove(device, otaPackageType, Collections.singletonList(getAttributeKey(otaPackageType, URL))); + } else { + List attrToRemove = new ArrayList<>(); + attrToRemove.add(getAttributeKey(otaPackageType, SIZE)); + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM)); + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM)); + remove(device, otaPackageType, attrToRemove); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); + } telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { @Override @@ -299,20 +312,24 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { }); } - private void remove(Device device, OtaPackageType firmwareType) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, OtaPackageUtil.getAttributeKeys(firmwareType), + private void remove(Device device, OtaPackageType otaPackageType) { + remove(device, otaPackageType, OtaPackageUtil.getAttributeKeys(otaPackageType)); + } + + private void remove(Device device, OtaPackageType otaPackageType, List attributesKeys) { + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, attributesKeys, new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { - log.trace("[{}] Success remove target firmware attributes!", device.getId()); + log.trace("[{}] Success remove target {} attributes!", device.getId(), otaPackageType); Set keysToNotify = new HashSet<>(); - OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); + attributesKeys.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); } @Override public void onFailure(Throwable t) { - log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); + log.error("[{}] Failed to remove target {} attributes!", device.getId(), otaPackageType, t); } }); } diff --git a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java index 2cde0e2113..2c3e7f5200 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/DefaultTbResourceService.java @@ -157,6 +157,11 @@ public class DefaultTbResourceService implements TbResourceService { resourceService.deleteResourcesByTenantId(tenantId); } + @Override + public long sumDataSizeByTenantId(TenantId tenantId) { + return resourceService.sumDataSizeByTenantId(tenantId); + } + private Comparator getComparator(String sortProperty, String sortOrder) { Comparator comparator; if ("name".equals(sortProperty)) { diff --git a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java index 7ad1848138..d3d079f548 100644 --- a/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java +++ b/application/src/main/java/org/thingsboard/server/service/resource/TbResourceService.java @@ -55,4 +55,5 @@ public interface TbResourceService { void deleteResourcesByTenantId(TenantId tenantId); + long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 76bbe1f518..980c3d2c10 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -536,6 +536,9 @@ public class DefaultTransportApiService implements TransportApiService { if (otaPackageInfo == null) { builder.setResponseStatus(TransportProtos.ResponseStatus.NOT_FOUND); + } else if (otaPackageInfo.hasUrl()) { + builder.setResponseStatus(TransportProtos.ResponseStatus.FAILURE); + log.trace("[{}] Can`t send OtaPackage with URL data!", otaPackageInfo.getId()); } else { builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); builder.setOtaPackageIdMSB(otaPackageId.getId().getMostSignificantBits()); diff --git a/application/src/test/java/org/thingsboard/server/service/resource/BaseTbResourceServiceTest.java b/application/src/test/java/org/thingsboard/server/service/resource/BaseTbResourceServiceTest.java index 464ca5c3cf..62facbb424 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/BaseTbResourceServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/BaseTbResourceServiceTest.java @@ -19,17 +19,24 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.EntityInfo; +import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; import org.thingsboard.server.common.data.TbResourceInfo; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; @@ -109,6 +116,64 @@ public class BaseTbResourceServiceTest extends AbstractControllerTest { .andExpect(status().isOk()); } + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testSaveResourceWithMaxSumDataSizeOutOfLimit() throws Exception { + loginSysAdmin(); + long limit = 1; + EntityInfo defaultTenantProfileInfo = doGet("/api/tenantProfileInfo/default", EntityInfo.class); + TenantProfile defaultTenantProfile = doGet("/api/tenantProfile/" + defaultTenantProfileInfo.getId().getId().toString(), TenantProfile.class); + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(limit).build()); + doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class); + + loginTenantAdmin(); + + Assert.assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId)); + + createResource("test", DEFAULT_FILE_NAME); + + Assert.assertEquals(1, resourceService.sumDataSizeByTenantId(tenantId)); + + try { + thrown.expect(DataValidationException.class); + thrown.expectMessage(String.format("Failed to create the tb resource, files size limit is exhausted %d bytes!", limit)); + createResource("test1", 1 + DEFAULT_FILE_NAME); + } finally { + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxResourcesInBytes(0).build()); + loginSysAdmin(); + doPost("/api/tenantProfile", defaultTenantProfile, TenantProfile.class); + } + } + + @Test + public void sumDataSizeByTenantId() throws ThingsboardException { + Assert.assertEquals(0, resourceService.sumDataSizeByTenantId(tenantId)); + + createResource("test", DEFAULT_FILE_NAME); + Assert.assertEquals(1, resourceService.sumDataSizeByTenantId(tenantId)); + + int maxSumDataSize = 8; + + for (int i = 2; i <= maxSumDataSize; i++) { + createResource("test" + i, i + DEFAULT_FILE_NAME); + Assert.assertEquals(i, resourceService.sumDataSizeByTenantId(tenantId)); + } + + Assert.assertEquals(maxSumDataSize, resourceService.sumDataSizeByTenantId(tenantId)); + } + + private TbResource createResource(String title, String filename) throws ThingsboardException { + TbResource resource = new TbResource(); + resource.setTenantId(tenantId); + resource.setTitle(title); + resource.setResourceType(ResourceType.JKS); + resource.setFileName(filename); + resource.setData("1"); + return resourceService.saveResource(resource); + } + @Test public void testSaveTbResource() throws Exception { TbResource resource = new TbResource(); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java index 589bdf14b6..fea29681c1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/ota/OtaPackageService.java @@ -44,9 +44,11 @@ public interface OtaPackageService { PageData findTenantOtaPackagesByTenantId(TenantId tenantId, PageLink pageLink); - PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); + PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink); void deleteOtaPackage(TenantId tenantId, OtaPackageId otaPackageId); void deleteOtaPackagesByTenantId(TenantId tenantId); + + long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java index 802628ff2c..1694c89bae 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java @@ -49,5 +49,5 @@ public interface ResourceService { void deleteResourcesByTenantId(TenantId tenantId); - + long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java index 6110310cd3..3506aaea75 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackage.java @@ -37,8 +37,8 @@ public class OtaPackage extends OtaPackageInfo { super(id); } - public OtaPackage(OtaPackage firmware) { - super(firmware); - this.data = firmware.getData(); + public OtaPackage(OtaPackage otaPackage) { + super(otaPackage); + this.data = otaPackage.getData(); } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java index 5a33a95215..f27c90c20b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/OtaPackageInfo.java @@ -37,6 +37,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo implements Searc @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; + @Column(name = OTA_PACKAGE_URL_COLUMN) + private String url; + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; @@ -118,6 +122,7 @@ public class OtaPackageEntity extends BaseSqlEntity implements Searc this.type = firmware.getType(); this.title = firmware.getTitle(); this.version = firmware.getVersion(); + this.url = firmware.getUrl(); this.fileName = firmware.getFileName(); this.contentType = firmware.getContentType(); this.checksumAlgorithm = firmware.getChecksumAlgorithm(); @@ -148,6 +153,7 @@ public class OtaPackageEntity extends BaseSqlEntity implements Searc firmware.setType(type); firmware.setTitle(title); firmware.setVersion(version); + firmware.setUrl(url); firmware.setFileName(fileName); firmware.setContentType(contentType); firmware.setChecksumAlgorithm(checksumAlgorithm); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java index 30441ed098..db16251f71 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OtaPackageInfoEntity.java @@ -22,6 +22,7 @@ import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -50,6 +51,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TABLE_ import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TILE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_TYPE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_URL_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.OTA_PACKAGE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -76,6 +78,9 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen @Column(name = OTA_PACKAGE_VERSION_COLUMN) private String version; + @Column(name = OTA_PACKAGE_URL_COLUMN) + private String url; + @Column(name = OTA_PACKAGE_FILE_NAME_COLUMN) private String fileName; @@ -116,6 +121,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen } this.title = firmware.getTitle(); this.version = firmware.getVersion(); + this.url = firmware.getUrl(); this.fileName = firmware.getFileName(); this.contentType = firmware.getContentType(); this.checksumAlgorithm = firmware.getChecksumAlgorithm(); @@ -125,7 +131,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen } public OtaPackageInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, OtaPackageType type, String title, String version, - String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, + String url, String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, Object additionalInfo, boolean hasData) { this.id = id; this.createdTime = createdTime; @@ -134,6 +140,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen this.type = type; this.title = title; this.version = version; + this.url = url; this.fileName = fileName; this.contentType = contentType; this.checksumAlgorithm = checksumAlgorithm; @@ -164,6 +171,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity implemen firmware.setType(type); firmware.setTitle(title); firmware.setVersion(version); + firmware.setUrl(url); firmware.setFileName(fileName); firmware.setContentType(contentType); firmware.setChecksumAlgorithm(checksumAlgorithm); diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index 536c79843a..e06f2098eb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -20,28 +20,32 @@ import com.google.common.hash.Hashing; import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.Cache; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.device.DeviceProfileDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.nio.ByteBuffer; @@ -50,6 +54,7 @@ import java.util.List; import java.util.Optional; import static org.thingsboard.server.common.data.CacheConstants.OTA_PACKAGE_CACHE; +import static org.thingsboard.server.common.data.EntityType.OTA_PACKAGE; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validatePageLink; @@ -67,6 +72,10 @@ public class BaseOtaPackageService implements OtaPackageService { private final CacheManager cacheManager; private final OtaPackageDataCache otaPackageDataCache; + @Autowired + @Lazy + private TbTenantProfileCache tenantProfileCache; + @Override public OtaPackageInfo saveOtaPackageInfo(OtaPackageInfo otaPackageInfo) { log.trace("Executing saveOtaPackageInfo [{}]", otaPackageInfo); @@ -172,11 +181,11 @@ public class BaseOtaPackageService implements OtaPackageService { } @Override - public PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { - log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); + public PageData findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) { + log.trace("Executing findTenantOtaPackagesByTenantIdAndHasData, tenantId [{}], pageLink [{}]", tenantId, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validatePageLink(pageLink); - return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, hasData, pageLink); + return otaPackageInfoDao.findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, otaPackageType, pageLink); } @Override @@ -204,6 +213,11 @@ public class BaseOtaPackageService implements OtaPackageService { } } + @Override + public long sumDataSizeByTenantId(TenantId tenantId) { + return otaPackageDao.sumDataSizeByTenantId(tenantId); + } + @Override public void deleteOtaPackagesByTenantId(TenantId tenantId) { log.trace("Executing deleteOtaPackagesByTenantId, tenantId [{}]", tenantId); @@ -227,31 +241,43 @@ public class BaseOtaPackageService implements OtaPackageService { private DataValidator otaPackageValidator = new DataValidator<>() { + @Override + protected void validateCreate(TenantId tenantId, OtaPackage otaPackage) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); + } + @Override protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { validateImpl(otaPackage); - if (StringUtils.isEmpty(otaPackage.getFileName())) { - throw new DataValidationException("OtaPackage file name should be specified!"); - } + if (StringUtils.isEmpty(otaPackage.getUrl())) { + if (StringUtils.isEmpty(otaPackage.getFileName())) { + throw new DataValidationException("OtaPackage file name should be specified!"); + } - if (StringUtils.isEmpty(otaPackage.getContentType())) { - throw new DataValidationException("OtaPackage content type should be specified!"); - } + if (StringUtils.isEmpty(otaPackage.getContentType())) { + throw new DataValidationException("OtaPackage content type should be specified!"); + } - if (otaPackage.getChecksumAlgorithm() == null) { - throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); - } - if (StringUtils.isEmpty(otaPackage.getChecksum())) { - throw new DataValidationException("OtaPackage checksum should be specified!"); - } + if (otaPackage.getChecksumAlgorithm() == null) { + throw new DataValidationException("OtaPackage checksum algorithm should be specified!"); + } + if (StringUtils.isEmpty(otaPackage.getChecksum())) { + throw new DataValidationException("OtaPackage checksum should be specified!"); + } - String currentChecksum; + String currentChecksum; - currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); + currentChecksum = generateChecksum(otaPackage.getChecksumAlgorithm(), otaPackage.getData()); - if (!currentChecksum.equals(otaPackage.getChecksum())) { - throw new DataValidationException("Wrong otaPackage file!"); + if (!currentChecksum.equals(otaPackage.getChecksum())) { + throw new DataValidationException("Wrong otaPackage file!"); + } + } else { + //TODO: validate url } } @@ -264,6 +290,13 @@ public class BaseOtaPackageService implements OtaPackageService { if (otaPackageOld.getData() != null && !otaPackageOld.getData().equals(otaPackage.getData())) { throw new DataValidationException("Updating otaPackage data is prohibited!"); } + + if (otaPackageOld.getData() == null && otaPackage.getData() != null) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxOtaPackagesInBytes = profileConfiguration.getMaxOtaPackagesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, otaPackageDao, maxOtaPackagesInBytes, otaPackage.getDataSize(), OTA_PACKAGE); + } } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java index 42f66663d1..ef8740030c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageDao.java @@ -16,8 +16,11 @@ package org.thingsboard.server.dao.ota; import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityDao; +import org.thingsboard.server.dao.TenantEntityWithDataDao; -public interface OtaPackageDao extends Dao { - +public interface OtaPackageDao extends Dao, TenantEntityWithDataDao { + Long sumDataSizeByTenantId(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java index d3294f0ec3..c40accf00a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/OtaPackageInfoDao.java @@ -28,7 +28,7 @@ public interface OtaPackageInfoDao extends Dao { PageData findOtaPackageInfoByTenantId(TenantId tenantId, PageLink pageLink); - PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink); + PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink); boolean isOtaPackageUsed(OtaPackageId otaPackageId, OtaPackageType otaPackageType, DeviceProfileId deviceProfileId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java index 6dacd1357f..f442f5acea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.hibernate.exception.ConstraintViolationException; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.TbResource; @@ -28,16 +29,19 @@ import org.thingsboard.server.common.data.id.TbResourceId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; import org.thingsboard.server.dao.service.Validator; +import org.thingsboard.server.dao.tenant.TbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantDao; import java.util.List; import java.util.Optional; +import static org.thingsboard.server.common.data.EntityType.TB_RESOURCE; import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; import static org.thingsboard.server.dao.service.Validator.validateId; @@ -49,12 +53,13 @@ public class BaseResourceService implements ResourceService { private final TbResourceDao resourceDao; private final TbResourceInfoDao resourceInfoDao; private final TenantDao tenantDao; + private final TbTenantProfileCache tenantProfileCache; - - public BaseResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao) { + public BaseResourceService(TbResourceDao resourceDao, TbResourceInfoDao resourceInfoDao, TenantDao tenantDao, @Lazy TbTenantProfileCache tenantProfileCache) { this.resourceDao = resourceDao; this.resourceInfoDao = resourceInfoDao; this.tenantDao = tenantDao; + this.tenantProfileCache = tenantProfileCache; } @Override @@ -143,8 +148,23 @@ public class BaseResourceService implements ResourceService { tenantResourcesRemover.removeEntities(tenantId, tenantId); } + @Override + public long sumDataSizeByTenantId(TenantId tenantId) { + return resourceDao.sumDataSizeByTenantId(tenantId); + } + private DataValidator resourceValidator = new DataValidator<>() { + @Override + protected void validateCreate(TenantId tenantId, TbResource resource) { + if (tenantId != null && !TenantId.SYS_TENANT_ID.equals(tenantId) ) { + DefaultTenantProfileConfiguration profileConfiguration = + (DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration(); + long maxSumResourcesDataInBytes = profileConfiguration.getMaxResourcesInBytes(); + validateMaxSumDataSizePerTenant(tenantId, resourceDao, maxSumResourcesDataInBytes, resource.getData().length(), TB_RESOURCE); + } + } + @Override protected void validateDataImpl(TenantId tenantId, TbResource resource) { if (StringUtils.isEmpty(resource.getTitle())) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java index 230e104191..f0f3f3a1e5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/resource/TbResourceDao.java @@ -21,10 +21,11 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.dao.TenantEntityWithDataDao; import java.util.List; -public interface TbResourceDao extends Dao { +public interface TbResourceDao extends Dao, TenantEntityWithDataDao { TbResource getResource(TenantId tenantId, ResourceType resourceType, String resourceId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java index ff8e79efc2..e630624ed4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/DataValidator.java @@ -23,8 +23,10 @@ import org.hibernate.validator.cfg.ConstraintMapping; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.common.data.validation.NoXss; import org.thingsboard.server.dao.TenantEntityDao; +import org.thingsboard.server.dao.TenantEntityWithDataDao; import org.thingsboard.server.dao.exception.DataValidationException; import javax.validation.ConstraintViolation; @@ -123,6 +125,19 @@ public abstract class DataValidator> { } } + protected void validateMaxSumDataSizePerTenant(TenantId tenantId, + TenantEntityWithDataDao dataDao, + long maxSumDataSize, + long currentDataSize, + EntityType entityType) { + if (maxSumDataSize > 0) { + if (dataDao.sumDataSizeByTenantId(tenantId) + currentDataSize > maxSumDataSize) { + throw new DataValidationException(String.format("Failed to create the %s, files size limit is exhausted %d bytes!", + entityType.name().toLowerCase().replaceAll("_", " "), maxSumDataSize)); + } + } + } + protected static void validateJsonStructure(JsonNode expectedNode, JsonNode actualNode) { Set expectedFields = new HashSet<>(); Iterator fieldsIterator = expectedNode.fieldNames(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java index 95737ca48d..98309b9e51 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/JpaOtaPackageDao.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.OtaPackage; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.ota.OtaPackageDao; import org.thingsboard.server.dao.model.sql.OtaPackageEntity; import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; @@ -43,4 +44,8 @@ public class JpaOtaPackageDao extends JpaAbstractSearchTextDao findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, boolean hasData, PageLink pageLink) { + public PageData findOtaPackageInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, OtaPackageType otaPackageType, PageLink pageLink) { return DaoUtil.toPageData(otaPackageInfoRepository .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( tenantId.getId(), deviceProfileId.getId(), otaPackageType, - hasData, Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java index 9848f83200..b380f8a150 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageInfoRepository.java @@ -26,27 +26,26 @@ import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; import java.util.UUID; public interface OtaPackageInfoRepository extends CrudRepository { - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE " + "f.tenantId = :tenantId " + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") Page findAllByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE " + + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, true) FROM OtaPackageEntity f WHERE " + "f.tenantId = :tenantId " + "AND f.deviceProfileId = :deviceProfileId " + "AND f.type = :type " + - "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + + "AND (f.data IS NOT NULL OR f.url IS NOT NULL) " + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") OtaPackageType type, - @Param("hasData") boolean hasData, @Param("searchText") String searchText, Pageable pageable); - @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM OtaPackageEntity f WHERE f.id = :id") + @Query("SELECT new OtaPackageInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.url, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, CASE WHEN (f.data IS NOT NULL OR f.url IS NOT NULL) THEN true ELSE false END) FROM OtaPackageEntity f WHERE f.id = :id") OtaPackageInfoEntity findOtaPackageInfoById(@Param("id") UUID id); @Query(value = "SELECT exists(SELECT * " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java index 3699005ff2..47e6f82285 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/ota/OtaPackageRepository.java @@ -15,10 +15,15 @@ */ package org.thingsboard.server.dao.sql.ota; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; import org.thingsboard.server.dao.model.sql.OtaPackageEntity; +import org.thingsboard.server.dao.model.sql.OtaPackageInfoEntity; import java.util.UUID; public interface OtaPackageRepository extends CrudRepository { + @Query(value = "SELECT COALESCE(SUM(ota.data_size), 0) FROM ota_package ota WHERE ota.tenant_id = :tenantId AND ota.data IS NOT NULL", nativeQuery = true) + Long sumDataSizeByTenantId(@Param("tenantId") UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java index f35f654f77..324549765e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/resource/JpaTbResourceDao.java @@ -92,4 +92,8 @@ public class JpaTbResourceDao extends JpaAbstractSearchTextDao implements Comparator { @Override public int compare(D o1, D o2) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java index ab895e1052..35063eef75 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOtaPackageServiceTest.java @@ -28,11 +28,13 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.OtaPackage; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; +import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration; import org.thingsboard.server.dao.exception.DataValidationException; import java.nio.ByteBuffer; @@ -50,7 +52,9 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { private static final String CONTENT_TYPE = "text/plain"; private static final ChecksumAlgorithm CHECKSUM_ALGORITHM = ChecksumAlgorithm.SHA256; private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; - private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); + private static final long DATA_SIZE = 1L; + private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{(int) DATA_SIZE}); + private static final String URL = "http://firmware.test.org"; private IdComparator idComparator = new IdComparator<>(); @@ -78,6 +82,41 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @After public void after() { tenantService.deleteTenant(tenantId); + tenantProfileService.deleteTenantProfiles(tenantId); + } + + @Test + public void testSaveOtaPackageWithMaxSumDataSizeOutOfLimit() { + TenantProfile defaultTenantProfile = tenantProfileService.findDefaultTenantProfile(tenantId); + defaultTenantProfile.getProfileData().setConfiguration(DefaultTenantProfileConfiguration.builder().maxOtaPackagesInBytes(DATA_SIZE).build()); + tenantProfileService.saveTenantProfile(tenantId, defaultTenantProfile); + + Assert.assertEquals(0, otaPackageService.sumDataSizeByTenantId(tenantId)); + + createFirmware(tenantId, "1"); + Assert.assertEquals(1, otaPackageService.sumDataSizeByTenantId(tenantId)); + + thrown.expect(DataValidationException.class); + thrown.expectMessage(String.format("Failed to create the ota package, files size limit is exhausted %d bytes!", DATA_SIZE)); + createFirmware(tenantId, "2"); + } + + @Test + public void sumDataSizeByTenantId() { + Assert.assertEquals(0, otaPackageService.sumDataSizeByTenantId(tenantId)); + + createFirmware(tenantId, "0.1"); + Assert.assertEquals(1, otaPackageService.sumDataSizeByTenantId(tenantId)); + + int maxSumDataSize = 8; + List packages = new ArrayList<>(maxSumDataSize); + + for (int i = 2; i <= maxSumDataSize; i++) { + packages.add(createFirmware(tenantId, "0." + i)); + Assert.assertEquals(i, otaPackageService.sumDataSizeByTenantId(tenantId)); + } + + Assert.assertEquals(maxSumDataSize, otaPackageService.sumDataSizeByTenantId(tenantId)); } @Test @@ -93,6 +132,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + firmware.setDataSize(DATA_SIZE); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); Assert.assertNotNull(savedFirmware); @@ -113,6 +153,35 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); } + @Test + public void testSaveFirmwareWithUrl() { + OtaPackageInfo firmware = new OtaPackageInfo(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); + firmware.setTitle(TITLE); + firmware.setVersion(VERSION); + firmware.setUrl(URL); + firmware.setDataSize(0L); + OtaPackageInfo savedFirmware = otaPackageService.saveOtaPackageInfo(firmware); + + Assert.assertNotNull(savedFirmware); + Assert.assertNotNull(savedFirmware.getId()); + Assert.assertTrue(savedFirmware.getCreatedTime() > 0); + Assert.assertEquals(firmware.getTenantId(), savedFirmware.getTenantId()); + Assert.assertEquals(firmware.getTitle(), savedFirmware.getTitle()); + Assert.assertEquals(firmware.getFileName(), savedFirmware.getFileName()); + Assert.assertEquals(firmware.getContentType(), savedFirmware.getContentType()); + + savedFirmware.setAdditionalInfo(JacksonUtil.newObjectNode()); + otaPackageService.saveOtaPackageInfo(savedFirmware); + + OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); + Assert.assertEquals(foundFirmware.getTitle(), savedFirmware.getTitle()); + + otaPackageService.deleteOtaPackage(tenantId, savedFirmware.getId()); + } + @Test public void testSaveFirmwareInfoAndUpdateWithData() { OtaPackageInfo firmwareInfo = new OtaPackageInfo(); @@ -141,6 +210,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + firmware.setDataSize(DATA_SIZE); otaPackageService.saveOtaPackage(firmware); @@ -345,50 +415,15 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testSaveFirmwareWithExistingTitleAndVersion() { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - otaPackageService.saveOtaPackage(firmware); - - OtaPackage newFirmware = new OtaPackage(); - newFirmware.setTenantId(tenantId); - newFirmware.setDeviceProfileId(deviceProfileId); - newFirmware.setType(FIRMWARE); - newFirmware.setTitle(TITLE); - newFirmware.setVersion(VERSION); - newFirmware.setFileName(FILE_NAME); - newFirmware.setContentType(CONTENT_TYPE); - newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - newFirmware.setChecksum(CHECKSUM); - newFirmware.setData(DATA); - + createFirmware(tenantId, VERSION); thrown.expect(DataValidationException.class); thrown.expectMessage("OtaPackage with such title and version already exists!"); - otaPackageService.saveOtaPackage(newFirmware); + createFirmware(tenantId, VERSION); } @Test public void testDeleteFirmwareWithReferenceByDevice() { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + OtaPackage savedFirmware = createFirmware(tenantId, VERSION); Device device = new Device(); device.setTenantId(tenantId); @@ -409,18 +444,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testUpdateDeviceProfileId() { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + OtaPackage savedFirmware = createFirmware(tenantId, VERSION); try { thrown.expect(DataValidationException.class); @@ -448,6 +472,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + firmware.setDataSize(DATA_SIZE); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDeviceProfile.setFirmwareId(savedFirmware.getId()); @@ -465,18 +490,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testFindFirmwareById() { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + OtaPackage savedFirmware = createFirmware(tenantId, VERSION); OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); @@ -502,18 +516,7 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { @Test public void testDeleteFirmware() { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); + OtaPackage savedFirmware = createFirmware(tenantId, VERSION); OtaPackage foundFirmware = otaPackageService.findOtaPackageById(tenantId, savedFirmware.getId()); Assert.assertNotNull(foundFirmware); @@ -526,23 +529,25 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { public void testFindTenantFirmwaresByTenantId() { List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - OtaPackage firmware = new OtaPackage(); - firmware.setTenantId(tenantId); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(TITLE); - firmware.setVersion(VERSION + i); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - - OtaPackageInfo info = new OtaPackageInfo(otaPackageService.saveOtaPackage(firmware)); + OtaPackageInfo info = new OtaPackageInfo(createFirmware(tenantId, VERSION + i)); info.setHasData(true); firmwares.add(info); } + OtaPackageInfo firmwareWithUrl = new OtaPackageInfo(); + firmwareWithUrl.setTenantId(tenantId); + firmwareWithUrl.setDeviceProfileId(deviceProfileId); + firmwareWithUrl.setType(FIRMWARE); + firmwareWithUrl.setTitle(TITLE); + firmwareWithUrl.setVersion(VERSION); + firmwareWithUrl.setUrl(URL); + firmwareWithUrl.setDataSize(0L); + + OtaPackageInfo savedFwWithUrl = otaPackageService.saveOtaPackageInfo(firmwareWithUrl); + savedFwWithUrl.setHasData(true); + + firmwares.add(savedFwWithUrl); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); PageData pageData; @@ -571,58 +576,38 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { public void testFindTenantFirmwaresByTenantIdAndHasData() { List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { - OtaPackageInfo firmwareInfo = new OtaPackageInfo(); - firmwareInfo.setTenantId(tenantId); - firmwareInfo.setDeviceProfileId(deviceProfileId); - firmwareInfo.setType(FIRMWARE); - firmwareInfo.setTitle(TITLE); - firmwareInfo.setVersion(VERSION + i); - firmwareInfo.setFileName(FILE_NAME); - firmwareInfo.setContentType(CONTENT_TYPE); - firmwareInfo.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmwareInfo.setChecksum(CHECKSUM); - firmwareInfo.setDataSize((long) DATA.array().length); - firmwares.add(otaPackageService.saveOtaPackageInfo(firmwareInfo)); + firmwares.add(new OtaPackageInfo(otaPackageService.saveOtaPackage(createFirmware(tenantId, VERSION + i)))); } + OtaPackageInfo firmwareWithUrl = new OtaPackageInfo(); + firmwareWithUrl.setTenantId(tenantId); + firmwareWithUrl.setDeviceProfileId(deviceProfileId); + firmwareWithUrl.setType(FIRMWARE); + firmwareWithUrl.setTitle(TITLE); + firmwareWithUrl.setVersion(VERSION); + firmwareWithUrl.setUrl(URL); + firmwareWithUrl.setDataSize(0L); + + OtaPackageInfo savedFwWithUrl = otaPackageService.saveOtaPackageInfo(firmwareWithUrl); + savedFwWithUrl.setHasData(true); + + firmwares.add(savedFwWithUrl); + List loadedFirmwares = new ArrayList<>(); PageLink pageLink = new PageLink(16); PageData pageData; do { - pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - Collections.sort(firmwares, idComparator); - Collections.sort(loadedFirmwares, idComparator); - - Assert.assertEquals(firmwares, loadedFirmwares); - - firmwares.forEach(f -> { - OtaPackage firmware = new OtaPackage(f.getId()); - firmware.setCreatedTime(f.getCreatedTime()); - firmware.setTenantId(f.getTenantId()); - firmware.setDeviceProfileId(deviceProfileId); - firmware.setType(FIRMWARE); - firmware.setTitle(f.getTitle()); - firmware.setVersion(f.getVersion()); - firmware.setFileName(FILE_NAME); - firmware.setContentType(CONTENT_TYPE); - firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); - firmware.setChecksum(CHECKSUM); - firmware.setData(DATA); - firmware.setDataSize((long) DATA.array().length); - otaPackageService.saveOtaPackage(firmware); - f.setHasData(true); - }); - loadedFirmwares = new ArrayList<>(); pageLink = new PageLink(16); do { - pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); + pageData = otaPackageService.findTenantOtaPackagesByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -642,4 +627,20 @@ public abstract class BaseOtaPackageServiceTest extends AbstractServiceTest { Assert.assertTrue(pageData.getData().isEmpty()); } + private OtaPackage createFirmware(TenantId tenantId, String version) { + OtaPackage firmware = new OtaPackage(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); + firmware.setTitle(TITLE); + firmware.setVersion(version); + firmware.setFileName(FILE_NAME); + firmware.setContentType(CONTENT_TYPE); + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + firmware.setChecksum(CHECKSUM); + firmware.setData(DATA); + firmware.setDataSize(DATA_SIZE); + return otaPackageService.saveOtaPackage(firmware); + } + } diff --git a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java index 7a0e3cce63..43e3a5e329 100644 --- a/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java +++ b/rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/TbContext.java @@ -47,7 +47,9 @@ import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.nosql.CassandraStatementTask; import org.thingsboard.server.dao.nosql.TbResultSetFuture; +import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.relation.RelationService; +import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -202,6 +204,10 @@ public interface TbContext { EntityViewService getEntityViewService(); + ResourceService getResourceService(); + + OtaPackageService getOtaPackageService(); + RuleEngineDeviceProfileCache getDeviceProfileCache(); EdgeService getEdgeService(); diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 34993e5318..6afeb6e78d 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -40,7 +40,7 @@ export class OtaPackageService { public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, hasData = true, config?: RequestConfig): Observable> { - const url = `/api/otaPackages/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; + const url = `/api/otaPackages/${deviceProfileId}/${type}${pageLink.toQuery()}`; return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html index 13e3976299..dfa4943b4b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.html @@ -88,6 +88,30 @@ {{ 'tenant-profile.maximum-rule-chains-range' | translate}} + + tenant-profile.maximum-resources-sum-data-size + + + {{ 'tenant-profile.maximum-resources-sum-data-size-required' | translate}} + + + {{ 'tenant-profile.maximum-resources-sum-data-size-range' | translate}} + + + + tenant-profile.maximum-ota-packages-sum-data-size + + + {{ 'tenant-profile.maximum-ota-packages-sum-data-size-required' | translate}} + + + {{ 'tenant-profile.maximum-ota-packages-sum-data-size-range' | translate}} + + tenant-profile.max-transport-messages Date: Thu, 3 Jun 2021 18:54:09 +0300 Subject: [PATCH 37/75] Improve mobile app support --- ui-ngx/src/app/core/api/widget-api.models.ts | 1 + ui-ngx/src/app/core/auth/auth.service.ts | 34 ++++-- ui-ngx/src/app/core/guards/auth.guard.ts | 6 + .../src/app/core/services/mobile.service.ts | 103 +++++++++++++++++- ui-ngx/src/app/core/utils.ts | 4 + .../dashboard-page.component.html | 2 +- .../dashboard-page.component.ts | 14 +++ .../default-state-controller.component.ts | 4 + .../entity-state-controller.component.ts | 4 + .../states/state-controller.component.ts | 2 + .../app/shared/models/window-message.model.ts | 14 ++- ui-ngx/src/theme.scss | 47 ++++++++ 12 files changed, 224 insertions(+), 11 deletions(-) diff --git a/ui-ngx/src/app/core/api/widget-api.models.ts b/ui-ngx/src/app/core/api/widget-api.models.ts index 8c6ac47ba5..515e5397f1 100644 --- a/ui-ngx/src/app/core/api/widget-api.models.ts +++ b/ui-ngx/src/app/core/api/widget-api.models.ts @@ -152,6 +152,7 @@ export interface IStateController { getStateIndex(): number; getStateIdAtIndex(index: number): string; getEntityId(entityParamName: string): EntityId; + getCurrentStateName(): string; } export interface SubscriptionInfo { diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index 4d25e5ce72..c17d993bf7 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -45,6 +45,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; +import { isMobileApp } from '@core/utils'; @Injectable({ providedIn: 'root' @@ -194,11 +195,13 @@ export class AuthService { } public gotoDefaultPlace(isAuthenticated: boolean) { - const authState = getCurrentAuthState(this.store); - const url = this.defaultUrl(isAuthenticated, authState); - this.zone.run(() => { - this.router.navigateByUrl(url); - }); + if (!isMobileApp()) { + const authState = getCurrentAuthState(this.store); + const url = this.defaultUrl(isAuthenticated, authState); + this.zone.run(() => { + this.router.navigateByUrl(url); + }); + } } public loadOAuth2Clients(): Observable> { @@ -516,12 +519,15 @@ export class AuthService { return this.refreshTokenSubject !== null; } - public setUserFromJwtToken(jwtToken, refreshToken, notify) { + public setUserFromJwtToken(jwtToken, refreshToken, notify): Observable { + const authenticatedSubject = new ReplaySubject(); if (!jwtToken) { AuthService.clearTokenData(); if (notify) { this.notifyUnauthenticated(); } + authenticatedSubject.next(false); + authenticatedSubject.complete(); } else { this.updateAndValidateTokens(jwtToken, refreshToken, true); if (notify) { @@ -530,16 +536,30 @@ export class AuthService { (authPayload) => { this.notifyUserLoaded(true); this.notifyAuthenticated(authPayload); + authenticatedSubject.next(true); + authenticatedSubject.complete(); }, () => { this.notifyUserLoaded(true); this.notifyUnauthenticated(); + authenticatedSubject.next(false); + authenticatedSubject.complete(); } ); } else { - this.loadUser(false).subscribe(); + this.loadUser(false).subscribe( + () => { + authenticatedSubject.next(true); + authenticatedSubject.complete(); + }, + () => { + authenticatedSubject.next(false); + authenticatedSubject.complete(); + } + ); } } + return authenticatedSubject; } private updateAndValidateTokens(jwtToken, refreshToken, notify: boolean) { diff --git a/ui-ngx/src/app/core/guards/auth.guard.ts b/ui-ngx/src/app/core/guards/auth.guard.ts index fdba07b94a..98fd4bd969 100644 --- a/ui-ngx/src/app/core/guards/auth.guard.ts +++ b/ui-ngx/src/app/core/guards/auth.guard.ts @@ -29,6 +29,7 @@ import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; import { UtilsService } from '@core/services/utils.service'; import { isObject } from '@core/utils'; +import { MobileService } from '@core/services/mobile.service'; @Injectable({ providedIn: 'root' @@ -41,6 +42,7 @@ export class AuthGuard implements CanActivate, CanActivateChild { private dialogService: DialogService, private utils: UtilsService, private translate: TranslateService, + private mobileService: MobileService, private zone: NgZone) {} getAuthState(): Observable { @@ -108,6 +110,10 @@ export class AuthGuard implements CanActivate, CanActivateChild { return of(false); } } + if (this.mobileService.isMobileApp() && !path.startsWith('dashboard.')) { + this.mobileService.handleMobileNavigation(path, params); + return of(false); + } const defaultUrl = this.authService.defaultUrl(true, authState, path, params); if (defaultUrl) { // this.authService.gotoDefaultPlace(true); diff --git a/ui-ngx/src/app/core/services/mobile.service.ts b/ui-ngx/src/app/core/services/mobile.service.ts index b6774f37d0..800651356a 100644 --- a/ui-ngx/src/app/core/services/mobile.service.ts +++ b/ui-ngx/src/app/core/services/mobile.service.ts @@ -20,9 +20,14 @@ import { isDefined } from '@core/utils'; import { MobileActionResult, WidgetMobileActionResult, WidgetMobileActionType } from '@shared/models/widget.models'; import { from, of } from 'rxjs'; import { Observable } from 'rxjs/internal/Observable'; -import { catchError } from 'rxjs/operators'; +import { catchError, tap } from 'rxjs/operators'; +import { OpenDashboardMessage, ReloadUserMessage, WindowMessage } from '@shared/models/window-message.model'; +import { Params, Router } from '@angular/router'; +import { AuthService } from '@core/auth/auth.service'; const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; +const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; +const navigationHandler = 'tbMobileNavigationHandler'; const mobileHandler = 'tbMobileHandler'; // @dynamic @@ -34,10 +39,20 @@ export class MobileService { private readonly mobileApp; private readonly mobileChannel; - constructor(@Inject(WINDOW) private window: Window) { + private readonly onWindowMessageListener = this.onWindowMessage.bind(this); + + private reloadUserObservable: Observable; + private lastDashboardId: string; + + constructor(@Inject(WINDOW) private window: Window, + private router: Router, + private authService: AuthService) { const w = (this.window as any); this.mobileChannel = w.flutter_inappwebview; this.mobileApp = isDefined(this.mobileChannel); + if (this.mobileApp) { + window.addEventListener('message', this.onWindowMessageListener); + } } public isMobileApp(): boolean { @@ -50,6 +65,12 @@ export class MobileService { } } + public onDashboardLoaded() { + if (this.mobileApp) { + this.mobileChannel.callHandler(dashboardLoadedHandler); + } + } + public handleWidgetMobileAction(type: WidgetMobileActionType, ...args: any[]): Observable> { if (this.mobileApp) { @@ -67,4 +88,82 @@ export class MobileService { } } + public handleMobileNavigation(path?: string, params?: Params) { + if (this.mobileApp) { + this.mobileChannel.callHandler(navigationHandler, path, params); + } + } + + private onWindowMessage(event: MessageEvent) { + if (event.data) { + let message: WindowMessage; + try { + message = JSON.parse(event.data); + } catch (e) {} + if (message && message.type) { + switch (message.type) { + case 'openDashboardMessage': + const openDashboardMessage: OpenDashboardMessage = message.data; + this.openDashboard(openDashboardMessage); + break; + case 'reloadUserMessage': + const reloadUserMessage: ReloadUserMessage = message.data; + this.reloadUser(reloadUserMessage); + break; + } + } + } + } + + private openDashboard(openDashboardMessage: OpenDashboardMessage) { + if (openDashboardMessage && openDashboardMessage.dashboardId) { + if (this.reloadUserObservable) { + this.reloadUserObservable.subscribe( + (authenticated) => { + if (authenticated) { + this.doDashboardNavigation(openDashboardMessage); + } + } + ); + } else { + this.doDashboardNavigation(openDashboardMessage); + } + } + } + + private doDashboardNavigation(openDashboardMessage: OpenDashboardMessage) { + let url = `/dashboard/${openDashboardMessage.dashboardId}`; + const params = []; + if (openDashboardMessage.state) { + params.push(`state=${openDashboardMessage.state}`); + } + if (openDashboardMessage.embedded) { + params.push(`embedded=true`); + } + if (openDashboardMessage.hideToolbar) { + params.push(`hideToolbar=true`); + } + if (this.lastDashboardId === openDashboardMessage.dashboardId) { + params.push(`reload=${new Date().getTime()}`); + } + if (params.length) { + url += `?${params.join('&')}`; + } + this.lastDashboardId = openDashboardMessage.dashboardId; + this.router.navigateByUrl(url, {replaceUrl: true}); + } + + private reloadUser(reloadUserMessage: ReloadUserMessage) { + if (reloadUserMessage && reloadUserMessage.accessToken && reloadUserMessage.refreshToken) { + this.reloadUserObservable = this.authService.setUserFromJwtToken(reloadUserMessage.accessToken, + reloadUserMessage.refreshToken, true).pipe( + tap( + () => { + this.reloadUserObservable = null; + } + ) + ); + } + } + } diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 6291438a58..a287802ec2 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -441,3 +441,7 @@ export function generateSecret(length?: number): string { export function validateEntityId(entityId: EntityId | null): boolean { return isDefinedAndNotNull(entityId?.id) && entityId.id !== NULL_UUID && isDefinedAndNotNull(entityId?.entityType); } + +export function isMobileApp(): boolean { + return isDefined((window as any).flutter_inappwebview); +} diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html index 5c54fe23e0..8ff6944856 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.html @@ -87,7 +87,7 @@ (click)="updateDashboardImage($event)"> wallpaper - @@ -49,7 +49,7 @@
-
+
ota-update.title @@ -83,44 +83,68 @@ -
ota-update.warning-after-save-no-edit
-
- - ota-update.checksum-algorithm - - - {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} - - - - - ota-update.checksum - - -
- - +
ota-update.warning-after-save-no-edit
+ + Upload binary file + Use external URL +
-
-
+
+
+ + + + {{ 'ota-update.auto-generate-checksum' | translate }} + +
+
- ota-update.file-name - + ota-update.checksum-algorithm + + + {{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }} + + - ota-update.file-size-bytes - - - - ota-update.content-type - + ota-update.checksum + + ota-update.checksum-hint
+
+
+ + ota-update.file-name + + + + ota-update.file-size-bytes + + + + ota-update.content-type + + +
+
+
+
+ + ota-update.url + + + ota-update.url-required + +
diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index fd4cd7ae27..42e0ffc33c 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -30,6 +30,8 @@ import { OtaUpdateTypeTranslationMap } from '@shared/models/ota-package.models'; import { ActionNotificationShow } from '@core/notification/notification.actions'; +import { filter, takeUntil } from 'rxjs/operators'; +import { isNotEmptyStr } from '@core/utils'; @Component({ selector: 'tb-ota-update', @@ -52,6 +54,26 @@ export class OtaUpdateComponent extends EntityComponent implements O super(store, fb, entityValue, entitiesTableConfigValue); } + ngOnInit() { + super.ngOnInit(); + this.entityForm.get('resource').valueChanges.pipe( + filter(() => this.isAdd), + takeUntil(this.destroy$) + ).subscribe((resource) => { + if (resource === 'file') { + this.entityForm.get('url').clearValidators(); + this.entityForm.get('file').setValidators(Validators.required); + this.entityForm.get('url').updateValueAndValidity({emitEvent: false}); + this.entityForm.get('file').updateValueAndValidity({emitEvent: false}); + } else { + this.entityForm.get('file').clearValidators(); + this.entityForm.get('url').setValidators(Validators.required); + this.entityForm.get('file').updateValueAndValidity({emitEvent: false}); + this.entityForm.get('url').updateValueAndValidity({emitEvent: false}); + } + }); + } + ngOnDestroy() { super.ngOnDestroy(); this.destroy$.next(); @@ -74,6 +96,8 @@ export class OtaUpdateComponent extends EntityComponent implements O deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)], + url: [entity ? entity.url : ''], + resource: ['file'], additionalInfo: this.fb.group( { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], @@ -82,6 +106,7 @@ export class OtaUpdateComponent extends EntityComponent implements O }); if (this.isAdd) { form.addControl('file', this.fb.control(null, Validators.required)); + form.addControl('generateChecksum', this.fb.control(true)); } else { form.addControl('fileName', this.fb.control(null)); form.addControl('dataSize', this.fb.control(null)); @@ -101,6 +126,8 @@ export class OtaUpdateComponent extends EntityComponent implements O fileName: entity.fileName, dataSize: entity.dataSize, contentType: entity.contentType, + url: entity.url, + resource: isNotEmptyStr(entity.url) ? 'url' : 'file', additionalInfo: { description: entity.additionalInfo ? entity.additionalInfo.description : '' } @@ -108,8 +135,6 @@ export class OtaUpdateComponent extends EntityComponent implements O if (!this.isAdd && this.entityForm.enabled) { this.entityForm.disable({emitEvent: false}); this.entityForm.get('additionalInfo').enable({emitEvent: false}); - // this.entityForm.get('dataSize').disable({emitEvent: false}); - // this.entityForm.get('contentType').disable({emitEvent: false}); } } @@ -124,14 +149,9 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } - onPackageChecksumCopied() { - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant('ota-update.checksum-copied-message'), - type: 'success', - duration: 750, - verticalPosition: 'bottom', - horizontalPosition: 'right' - })); + prepareFormValue(formValue: any): any { + delete formValue.resource; + delete formValue.generateChecksum; + return super.prepareFormValue(formValue); } } diff --git a/ui-ngx/src/app/shared/models/ota-package.models.ts b/ui-ngx/src/app/shared/models/ota-package.models.ts index 22efc881b0..b6fecfc2c5 100644 --- a/ui-ngx/src/app/shared/models/ota-package.models.ts +++ b/ui-ngx/src/app/shared/models/ota-package.models.ts @@ -87,6 +87,7 @@ export interface OtaPackageInfo extends BaseData { title?: string; version?: string; hasData?: boolean; + url?: string; fileName: string; checksum?: string; checksumAlgorithm?: ChecksumAlgorithm; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index b4f8c32101..732535dafe 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -575,7 +575,8 @@ "enter-password": "Enter password", "enter-search": "Enter search", "created-time": "Created time", - "loading": "Loading..." + "loading": "Loading...", + "proceed": "Proceed" }, "content-type": { "json": "Json", @@ -2152,12 +2153,14 @@ "assign-firmware-required": "Assigned firmware is required", "assign-software": "Assigned software", "assign-software-required": "Assigned software is required", + "auto-generate-checksum": "Auto-generate checksum", "checksum": "Checksum", + "checksum-hint": "If checksum is empty, it will be generated automatically", "checksum-algorithm": "Checksum algorithm", "checksum-copied-message": "Package checksum has been copied to clipboard", - "change-firmware": "You have changed the firmware. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.", - "change-software": "You have changed the software. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.", - "chose-compatible-device-profile": "Choose compatible device profile. The uploaded package will be available only for devices with the chosen profile.", + "change-firmware": "Change of the firmware may cause update of { count, plural, 1 {1 device} other {# devices} }.", + "change-software": "Change of the software may cause update of { count, plural, 1 {1 device} other {# devices} }.", + "chose-compatible-device-profile": "The uploaded package will be available only for devices with the chosen profile.", "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", "chose-software-distributed-device": "Choose software that will be distributed to the devices", "content-type": "Content type", @@ -2193,6 +2196,8 @@ "firmware": "Firmware", "software": "Software" }, + "url": "Direct URL", + "url-required": "Direct URL is required", "version": "Version", "version-required": "Version is required.", "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." From 10dda013cbd9b990bc8fd3214f2d5cadc58935a1 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Thu, 3 Jun 2021 19:37:47 +0300 Subject: [PATCH 39/75] improvements --- .../ota/DefaultOtaPackageStateService.java | 32 +++++++++++++----- .../BaseOtaPackageControllerTest.java | 33 ++++++++++--------- .../server/dao/ota/BaseOtaPackageService.java | 2 +- .../server/dao/SqlDaoServiceTestSuite.java | 8 ++--- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java index 3bb85f0ffb..2ba4735d55 100644 --- a/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/ota/DefaultOtaPackageStateService.java @@ -285,18 +285,34 @@ public class DefaultOtaPackageStateService implements OtaPackageStateService { List attributes = new ArrayList<>(); attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, TITLE), otaPackage.getTitle()))); attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, VERSION), otaPackage.getVersion()))); - if (StringUtils.isEmpty(otaPackage.getUrl())) { + if (otaPackage.hasUrl()) { + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); + List attrToRemove = new ArrayList<>(); + + if (otaPackage.getDataSize() == null) { + attrToRemove.add(getAttributeKey(otaPackageType, SIZE)); + } else { + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); + } + + if (otaPackage.getChecksumAlgorithm() != null) { + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM)); + } else { + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); + } + + if (StringUtils.isEmpty(otaPackage.getChecksum())) { + attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM)); + } else { + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); + } + + remove(device, otaPackageType, attrToRemove); + } else { attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(otaPackageType, SIZE), otaPackage.getDataSize()))); attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM), otaPackage.getChecksumAlgorithm().name()))); attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, CHECKSUM), otaPackage.getChecksum()))); remove(device, otaPackageType, Collections.singletonList(getAttributeKey(otaPackageType, URL))); - } else { - List attrToRemove = new ArrayList<>(); - attrToRemove.add(getAttributeKey(otaPackageType, SIZE)); - attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM_ALGORITHM)); - attrToRemove.add(getAttributeKey(otaPackageType, CHECKSUM)); - remove(device, otaPackageType, attrToRemove); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(otaPackageType, URL), otaPackage.getUrl()))); } telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java index 9b73053831..a11d1b624a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseOtaPackageControllerTest.java @@ -50,7 +50,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes private static final String FILE_NAME = "filename.txt"; private static final String VERSION = "v1.0"; private static final String CONTENT_TYPE = "text/plain"; - private static final String CHECKSUM_ALGORITHM = "sha256"; + private static final String CHECKSUM_ALGORITHM = "SHA256"; private static final String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; private static final ByteBuffer DATA = ByteBuffer.wrap(new byte[]{1}); @@ -257,7 +257,7 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes @Test public void testFindTenantFirmwaresByHasData() throws Exception { List otaPackagesWithData = new ArrayList<>(); - List otaPackagesWithoutData = new ArrayList<>(); + List allOtaPackages = new ArrayList<>(); for (int i = 0; i < 165; i++) { OtaPackageInfo firmwareInfo = new OtaPackageInfo(); @@ -272,44 +272,45 @@ public abstract class BaseOtaPackageControllerTest extends AbstractControllerTes MockMultipartFile testData = new MockMultipartFile("file", FILE_NAME, CONTENT_TYPE, DATA.array()); OtaPackage savedFirmware = savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, CHECKSUM_ALGORITHM); - otaPackagesWithData.add(new OtaPackageInfo(savedFirmware)); - } else { - otaPackagesWithoutData.add(savedFirmwareInfo); + savedFirmwareInfo = new OtaPackageInfo(savedFirmware); + otaPackagesWithData.add(savedFirmwareInfo); } + + allOtaPackages.add(savedFirmwareInfo); } - List loadedFirmwaresWithData = new ArrayList<>(); + List loadedOtaPackagesWithData = new ArrayList<>(); PageLink pageLink = new PageLink(24); PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/true?", + pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE?", new TypeReference<>() { }, pageLink); - loadedFirmwaresWithData.addAll(pageData.getData()); + loadedOtaPackagesWithData.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - List loadedFirmwaresWithoutData = new ArrayList<>(); + List allLoadedOtaPackages = new ArrayList<>(); pageLink = new PageLink(24); do { - pageData = doGetTypedWithPageLink("/api/otaPackages/" + deviceProfileId.toString() + "/FIRMWARE/false?", + pageData = doGetTypedWithPageLink("/api/otaPackages?", new TypeReference<>() { }, pageLink); - loadedFirmwaresWithoutData.addAll(pageData.getData()); + allLoadedOtaPackages.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); Collections.sort(otaPackagesWithData, idComparator); - Collections.sort(otaPackagesWithoutData, idComparator); - Collections.sort(loadedFirmwaresWithData, idComparator); - Collections.sort(loadedFirmwaresWithoutData, idComparator); + Collections.sort(allOtaPackages, idComparator); + Collections.sort(loadedOtaPackagesWithData, idComparator); + Collections.sort(allLoadedOtaPackages, idComparator); - Assert.assertEquals(otaPackagesWithData, loadedFirmwaresWithData); - Assert.assertEquals(otaPackagesWithoutData, loadedFirmwaresWithoutData); + Assert.assertEquals(otaPackagesWithData, loadedOtaPackagesWithData); + Assert.assertEquals(allOtaPackages, allLoadedOtaPackages); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java index e06f2098eb..881baea4f1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/ota/BaseOtaPackageService.java @@ -253,7 +253,7 @@ public class BaseOtaPackageService implements OtaPackageService { protected void validateDataImpl(TenantId tenantId, OtaPackage otaPackage) { validateImpl(otaPackage); - if (StringUtils.isEmpty(otaPackage.getUrl())) { + if (!otaPackage.hasUrl()) { if (StringUtils.isEmpty(otaPackage.getFileName())) { throw new DataValidationException("OtaPackage file name should be specified!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index ac2618981b..f110aa2398 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,10 +24,10 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.sql.OtaPackageServiceSqlTest", -// "org.thingsboard.server.dao.service.attributes.sql.*SqlTest", -// "org.thingsboard.server.dao.service.event.sql.*SqlTest", -// "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest" + "org.thingsboard.server.dao.service.sql.*SqlTest", + "org.thingsboard.server.dao.service.attributes.sql.*SqlTest", + "org.thingsboard.server.dao.service.event.sql.*SqlTest", + "org.thingsboard.server.dao.service.timeseries.sql.*SqlTest" }) public class SqlDaoServiceTestSuite { From 1c22a4b35af2a556018eb21f78e7566357802e9c Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Fri, 4 Jun 2021 10:38:49 +0300 Subject: [PATCH 40/75] fixed tests --- .../thingsboard/server/dao/service/AbstractServiceTest.java | 4 ---- .../server/dao/service/BaseDeviceProfileServiceTest.java | 1 + .../thingsboard/server/dao/service/BaseDeviceServiceTest.java | 2 ++ 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java index 022051fe8c..e01c51e8ad 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/AbstractServiceTest.java @@ -59,7 +59,6 @@ import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.settings.AdminSettingsService; -import org.thingsboard.server.dao.tenant.DefaultTbTenantProfileCache; import org.thingsboard.server.dao.tenant.TenantProfileService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; @@ -162,9 +161,6 @@ public abstract class AbstractServiceTest { @Autowired protected OtaPackageService otaPackageService; - @Autowired - protected DefaultTbTenantProfileCache tenantProfileCache; - public class IdComparator implements Comparator { @Override public int compare(D o1, D o2) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index e53b740cbf..3f9f5edc36 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -109,6 +109,7 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); + firmware.setDataSize(1L); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); deviceProfile.setFirmwareId(savedFirmware.getId()); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index f335ffacd3..f1f081ad2c 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -200,6 +200,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); + firmware.setDataSize(1L); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); @@ -234,6 +235,7 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); firmware.setData(ByteBuffer.wrap(new byte[]{1})); + firmware.setDataSize(1L); OtaPackage savedFirmware = otaPackageService.saveOtaPackage(firmware); savedDevice.setFirmwareId(savedFirmware.getId()); From af0883f2c411fab5005c8ca38940332875c7cfd1 Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Fri, 4 Jun 2021 16:02:53 +0300 Subject: [PATCH 41/75] Fix typos in widgets descriptions --- .../src/main/data/json/system/widget_bundles/cards.json | 2 +- .../main/data/json/system/widget_bundles/control_widgets.json | 4 ++-- .../main/data/json/system/widget_bundles/digital_gauges.json | 2 +- .../data/json/system/widget_bundles/entity_admin_widgets.json | 4 ++-- .../main/data/json/system/widget_bundles/gateway_widgets.json | 2 +- .../main/data/json/system/widget_bundles/input_widgets.json | 2 +- .../src/main/data/json/system/widget_bundles/maps.json | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/cards.json b/application/src/main/data/json/system/widget_bundles/cards.json index 29b8b9f8d3..79f732bca8 100644 --- a/application/src/main/data/json/system/widget_bundles/cards.json +++ b/application/src/main/data/json/system/widget_bundles/cards.json @@ -28,7 +28,7 @@ "alias": "html_card", "name": "HTML Card", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAATFSURBVHja7d3tUxNXFMdx/u7vEkAJsKQK6bilKaBV1Eh5GGNaSyq0WodhRuqMtQJqW5UHCwKGMRJiQpL99cWGEGaaTjtjgaTnvNpz7mYnn8neu/duXtwWFbdWlxs8Vrf21VJcy5TU4FHKrBVbtjJqgshstayWmgFSWm1ZVlPEskEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAG+T9ACrlcLjjyc7lcWflcbfhBfnByOZfL5cpSKZfL+f/2G+WAJ/8dJAHh4GgFeK4BaiOjMYDtyskzAIvSQ+BtI0JmKif3NRBk3PM8Lwwhz/M8LxtAzgf30RoNBJEkjUOk0j4GwJok6XaDQ8LwtSSVXVrP1of4v1w978bu5YNh4fHlfnfwfpDo15Hevm/eHkCyP8Tc/rGV44d8FaLbl/QCRrrqQopXg251bkdSdjBIIpuSNAtAbwXyqgsA595Hh3Sm0+l0Or1QDzJxLWiYhCftdSFJoKMHiPnyvwTOhIHeD9KGA3SfIYBkOsGJtIPz/GNDauIvIfEFGJdKnZwtOPUgOw4kfT0E1vQMmPb12IEZaRJCz1RKBpBJCG+ocAVixw25XArTUdACTBbqdvY56MhLisK8xisDXRyiUi+MStoDnqjUCT9Keg3Oh48MCcXj8Xg8PlwPMqQE/Kzr8HK3LiQBFyozhaI8uClJ8+AUi8CD6qi1DYymUqkksH7Mnf0zLcNIvo2In64LGYXhauLCHUlaAnb3gIUq5FXN7//qmCH98s8RmoXv9KYuZBI+ryZ9cFsKzsvngUdVyAowcDGIjWOGRKRpaINNrdWF3IUeX9LS/PymLsMlSZqCLikM01XIe+CnE3ogRqRtAE9arQtZCVqynbCoWXBWpfddMCaNgLsnLQSj1gVwM5Je3/RPAKIBYPYIJOp5nud5dw8uFIPQxO1eCOf1oRs6Jm71QOtG0FMiydFQAHkKnJlIjTgkTwIyB87uEUglbh5c6G13UGhdkvSyPUicB5J0HYBQe/Bkv+tUPnr/JCDZVq7obyF6d6MNnC+COdTmSCs4A78Hs5dkG3zyYqAy13oec8CJ/XZ6l7qFzfW9apJ/80dNsr5V2yNyGxt7tmY3iEEMYhCDGOTEIJnD6YtBDGKQpocsDrvR5K4kyX96PeoOzu5Lysbj8b2Zvv7SkepphgwBcD4vqRAPVlXR91IauASUVAhWgny6e7ohtEdCwcpdCaDTBYYDCED5sHrxdEOuFZRx4Ya06cC3ZS05sKI04EzM3demA1NlLTqweuo7ewI86U7lHdYQzCgNzEnSHXB9SYPw/amHpKBPGoFzqVQqFYUxpYEX0pHqeINAav4wvXoI+eyweq1BIDHorry+nT6E1FRnGgQyCtFqUxVyo7baGJBHlVG4mFivgRxU9xMbjQIpR4HBqYRLz84hpBwFhqYSLu5Og0C07VZ6dXTvEHKk2iAQZZOdQHcqX3NrSdlb1WrjLHXL26/T/j+s2prdIAYxiEEMYhCDGMQgBjGIQQxiEIMYxCAGMYhBDGIQgxjEIAYxiEEMYhCDGMQgBjGIQQxiEIMY5PRAmmaD4ObYsvndVst+c2yiXWppjm3NS/oTe0OjFEeU1MMAAAAASUVORK5CYII=", - "description": "Useful to inject custom HTML code. Designed to displays static information only.", + "description": "Useful to inject custom HTML code. Designed to display static information only.", "descriptor": { "type": "static", "sizeX": 7.5, diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 8fea056506..c8664c2eb9 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -10,7 +10,7 @@ "alias": "rpc_debug_terminal", "name": "RPC debug terminal", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAWcklEQVR42u2dB5QWRZeGi6QgklFUDJhFMCJGRFHMomMAMStiBvUXRTFgRjGLERMoiIBZ1GVXdNcF4yIe5agYdvVnfkAwIAoiIs4+/72na3u+NN8wAzsD73vmzOnur7rCrbfuvV3VfSuUlZXNnDnzmGOOadKkSRCEKgAKlZSUzJgxA1IFWNWyZUsJRagutGjRAlIFdJVkIVQvevbsGWQBhWpH06ZNJQRBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEITlRK9evc5KwAdAnTt3rlevXvz1oIMOir+efvrp3bp1a9WqVUYOderUOfDAAy+77LJLL7308MMPX2ONNYop99577+ULyaOPPrpwsvfff59kG264Ya0W8uDBg6+55pqq5PDRRx8hh3XXXbfWtPnzzz8vK4/PPvtsjz328F9ffvnljF9///33q6++Ot6+/vrrv/fee+kEX3zxxeabb15biHXqqafecsstK5S4a6655pIlS/7444+11lprtSPWEUcc0alTp/333//hhx/mdNasWa54nFj9+vXj19122+2MM8747rvvuHLiiSe6rnrrrbc4feyxxzp27Aifbr/9dk6nTp1aW4j10ksvkf8uu+yyQoVM/piC1VFjbbbZZtGu+RUXhBMLgxjTQymuvPrqqxzvs88+HH/44Ydps/jJJ59wccstt8wua+ONN7722mvvuOOOQw89NJtYlAIv7777buwp+aSJtd122w0YMIBbUDDRUjMYsL8bbbSRn+63336ctm/f3k/XXnvtiy66iFsw4htssAE/MWzSlWncuDEXUc/kT7L4cXn9+vVxD+65557bbrttr732iunJ7dxzz91mm22o4QEHHECIA27v0aMHgkKFDxs2jIsk23PPPWng0KFD995773jv3wx+3Ldv34svvrh58+a06L777jv77LMbNGiQtgBcf+CBB8hz6623XnWIBd59990CxOrSpQtX3n77bY6HDBnCMfJNZ4gfRl9uuummGQWhz3788UfS//nnn/z/4Ycf0sR6/PHHOcVkLF26lAMkmybW3LlzMSVuaseOHes/jRkzhlPI7aeuLI8//niOGzVq5PxetmzZX3/9VVpayvFdd92Vrs8666zz008/ebYLFix46qmnuNiwYcM33niDK4sXL+ZeDuCTp58/f/7ChQvnzZvHRe91DubMmUMyrzMFPfnkk/z3PDk47LDD/N5fDFHg3PLVV1/5XeCJJ57wn3bffXeSIZ9vvvlmqeGQQw5ZFYiFN3DaaafRbOSFiLOJhX0cNWoUVxhqnCIRjk844YRiCvIbUQNk0qFDB0JQRGIdd9xxrgXxQvjQG9ZyuvPOO0di3XTTTQzrTTbZBAeO01133bUwsWADxxMnTkQx8LTxyiuvZBMrpymEMU5r9CJ6jg7Gp2zdurUTi58YAEiDYePEgppemYEDB/rA8PowtDh94YUXchKLn2644QYai9jhNExC8vzknO7evXscwFOmTFl1nHfUQ9euXdPO+5dffonbRNtcuHCibdu2/Dpu3Lhi/CQHfbBo0aLowKZN4YQJE9K9i71zxy7bx+rfvz+nV155ZWFiTZo0KVLTn3yLJBbEpZsJveKnN954I7/yzOvEQo3VrVvXf3JiQQU/hWqcouz9tE2bNpxOmzYtH7FioITJkydzmn7cYeBh3+EcNaEvajexnn766eHDh0+fPp1jnID4a5pYSA0zdPnll8cISu7pp9PnAx6PPzDmdN4//fTTsixAlGxi4Zxx+tBDDxUmFhXmOJK4SGLh2EWDmwbqx4nFwIg3ZhALKnD6zjvv+ClqktOPP/64QmK9+eabnG611VZOx2eeecZdBQeWd1UwhfhVeAbof9fMOX2sNC655JLIgAgmbF5//XU3EBFYFszEt99+m5NYTuhBgwadlYI7zhnEYqaNU7zjwsT64IMPOI6Kp3iNheGDWzjU6ZpguFcOsbDdHJ9//vn4f5xSk1WEWOD555/nND7CFCZWVNcuCIAywytPW5MInr/w3lyCGWZ05MiRHJ933nnx0XLHHXdMO+877LBDmo6uQh588MG0IXb16cRyf86PAUTJR6wXX3yRn5hJ8VOYEb0cgGPnrFoJxKLV5M/zTdTxiHHVIRbTUXT/999/36xZswqJBW6++WYSoOSYZsQj4WGHU57Vs1PyjO1WlQl6Z1KkBYX+9ttveDC49nhRr732GjJl5iwS6+uvv+Z2suU6vpr3CisBPp2Lq/7oo4/6Q5aTCR8R1YtfjPrE8f/111/zEcvZSTOPPfZYN7XcSBHMlVMi1p97vTuLIVb0sZZPYzFx448OmAL8M44pfdWZbhg9ejRXYEkxxGKcMe/is6aAg6uuuiq9KBSB24u+8e7HqWJONa1voALd4Jmg85Bs1FjYUOY1oB0/zZ49211pVyduDZ15zz77bFpLodXoS39ww4Pk4M4778yuFZz2ykeKYDcZJ54tjykxw5VALCrj7ibjh8dPfwRu165drSRWtQB68XBeTFhUFCEuar5fERw9xBRl9k8+3ZBNWYIb5hQ3pgRPkSlZCM0DB72CLsxZKDmTLGOJs60hztOuTKy33npM3gahBoJ5fzy/K664gnnwU045BccFTcmkuSQjVAnMi6aXz3/++edo0QShqsA7YdmRORRZFkEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEGoFPiMvY0hRh6rjSAqTs5o4XzU///1QX2luiAdrbRG48gjj9x2223jKQFe09FB0iAsB6Fgxo8fnx2faEXjggsuqGyhxOT4W3kQ7zlYpNPtt98+Oz1Btmga4SeqXlsiRHiJRNAkrkQ1yoHYTDF0b00HUjj44IPT7ClQdXp35RMLLUJImcp2ObH89jAQughecuBhI/MRi/AhW2yxRZEai2BdhLLJpzzofkIp77TTToQzjTFzVjtiEWIKLUUkKihFIBcCUHl0ayKrEueTcMjEwS5ALBJfeOGFxGkl0J6bSMRNpNrrrrsOyjobiEqNXuQKIYGJAEuIDr+XuEWInitplZkBBj3VoFDCe6ADiMccLEQngRi8CAKzFG4gyUpKSuIpxCICFrlRLoGvvVGuYGJ0ZNfclEXTiJIVA68RaITIXgTcIhIYkdnqGWj49ddfTzQbYnE7L+l+v4Xo3zFOGIHdiFdIWwhS4lewvORP7K4zzzzTFRvC9JoQXpDRzq/EfiY4wDnnnEMRMLU2EQu5nGwgWhWhfB555BEEDc8IZIUcGZojRoyI2iKDWGgFwp1BGsiHgLzNWBzER7whIlET1JTQy8SFJ4DWUUcdReBQJE4gP9QDgkboRHRFXhSRL7ARWoTKUOi+++5LKV4TwvPRkRRB0cQoi1EtiyQWt5MtY4aedl1FzgQAi1G+g8VO9oDbDANaESzUJfei9ohrhRb0CIBElidoJREiSMkQ9TjTdD/yJIw2bPDIgEgG8VIKzSfsoAcrZPjBRWQOd+FWsLjzpLn11lsZqORG6xhC1JbQdsTJwVhTw1pDLNrPgECIffr0oeoYHdxYNAFUcFOCX4Viz0ksBpYHmU0Dasawjn4MsVAASATyBYvgTd/40PQi6JsYzTwb9FaG9wMRoxbhmGBllSKWm0LK9YjikcEZxKJfg4Xzp/TGBoaE7yNEZDmvMFFSGVqMGQiHdokGC2VDqyGW798BS9h5IJqIeIzSgklY1fQ2O/AsKnXA8ItBVmuTxsJOwSGGICFZGDfEv+ciCh+tc2SC2IsZxEKsKLZsPyCmJwojIzuDWASJhFiDDLGIAvLKIBbmhkj/ccsGuOsRJStLLCp2//33V0gsHsQonRhuHKOH6GYkg1KJXjn5IDeaBsM87lyUAGbaxQWTXCd5Jh7flf8wj/iUaF9260gTix034ikyjLtj1CZioZYhB3LEGHHg+0EgU8YQqgu3CXUVhZhBLARHHxPPmM7G8PkeJ2SFefXhSMhJQu/nJBZXPKKkF1EgzBDmIENjwRWUhDslFFH4YaK6iEUb4QFWD6UVfXz0jcdnpyFRVUdiMVaRD6YWZwsu4n2SDCHg5KWHB/IpQCx4jDUkPRMlqMxaQywEiuAI2E/VUVfsAONuB2oMtwASIGLvdcJ4jk+AxYyWlDT0FjrP/ST4RKhPOMoI9r1rchKLItCLFIFlxJWOdiQnGNz0OuViR5xPFMfeNRSB71VZ5z2DWG7sIlwCOTUW3Y8QML74SXgOSIzHETQKagxW0Rx/ToRYTxi46FYMPuFdcIq7Rls8GW2h+bhTjCsOGJnQMV0TnLBg+8TwKyWedNJJtUljFQA2qJhpGKSGzsh4UOfGnPFtMwCflnumB3auzKlaPDkfY4DnBhjmD5U0nOYXHhhxRiMjGW13f79CCXsEa2EVBFGN0VVsNIQCxitCsdWaSXChhgO1gReF54R5quGLP4IgCEJNAJ41z4n5XHj81prvkOKVV7aSmMg2CVZrc8lzSlz/Z8LX54R4FPcrLN+yKOEC4qGGFToeubked8st7JHwzJy95b2DCY58+0QsN3r37p3e67bqYNOXym49j5RYSWTChbav1t49mgMRMB3F9C7LZz7VxP42iIZFD1jFJIpPxjDvgsh8iYaVnMIrdCuCWKwKF97KlXX09BbOVQeTWHFXs0qBgSdi/ZNYPiXjK8QYL4gV54LRT8z8YteYPvXVUxQYKi2f1PxFKAY6y2eRWMzRQyPyJGffJwdioQ6ZA2Q6O24tThrfuJU1Vx7sPUP2PWSOnglopjp55yJnoQwA5ieZtmVgULpPLToXmXziJ9/shFa4Jmbyk8VviuP5jsTMoJI5V6ihjyL+e8q4PzlbXXDKjCgpWaLxLTaRHovuXmjcUVHE+j9i8XIfCgZL55PRdD8zwswEQgimlVk/8cUc519hQAIWAVneofOcWKzJsFLE6wOQhvUKDJYTi9lzlkToKiaUnbJxZhkCsVO86wwIzY24LEzZO+2y4au56FEyj8ssaC+K4JjFRKag0D3Ql1/hE1tm8p8a0iJaCrFYaWABgJdV0MrBpuNJyRV/PTAkC3/Mj8NRsvVlKyqP/ma+lIl4n7IXscoRi3UJhjsvMvh6H8TytQtWIXhLCT+MfiVZgV27outKPv5+VTSF9BBZ+YsM8Mn3dnON5XehUfwtgGxi0YsswqBpyBnepF8OywZjIG0KydZf8QPkxjsqfp1d4zCaMRnEQlUzeFge5iUWL9fBex8ZxPLVLVLGl7dYDOV2CEflRawcphCLgEp3kwSxMpxWtA5W0mnnpzlzw4zGZJFYzChCjvgig79wkvaxsCO+CJhNLN8RjheSqBsHhZeJMoiFEcRrjOVGvx5iwbnlI5ZbQFw9f0OBZT60F5qelxdYDhexchCLsUj3M7hzEivYMjtvDsE8eogV1nzvI9AxvrclFHFiAcyZP0iy4uavAEAsuAJR6Crsr6sTFKfvuMnLJN7BmGCsJ9XDxamwLfRxmlgMFYyyv+fJ+wLpjceri1j4W+gqt4kVEotGrUY7kKWdd4Y1byXgXOcklr9TAEXQK3GP02zg0JAApwohRueddxBgLbaMVXp/uQBioU5wluEoXevPmNALS4riwYR5B9ORVIk3TDCm0M57MR94h4KUFMoDgc+kUIS/QIG2Y7kXhqVfH/A25iQWx+mUVCwnsXg4wPNjhFA01e7Xr5+/PRaBLxGrhw+QflFHyCRizu1P0yBB9jsLGDXUT4Yty163b2SIpzAYNenvFPB4CG8qO+tIbv7SywpCke+AeGMrFJ2wkoCqQ8/5W/lMV7qeEIRqAI+izDBhYYuZ7hcEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEQRAEYXUCrxKzn0IN2YpjU96Eri2C48vO8Xy4Ysd85DC0MvfuZPfyR+zOK3m73C4OTC6OIJBkkpJXP4muPNn+ty6Y52l8Z7GCm9yX6JWV6cuyEFoWFOCjy1sTvki5vDLpFxIcPznuYEJuXGOJ1dkE94kd8wXWtMrce6g1lS+CTw5hOh9+2cUXQ3jJLhJDfSnfe9nF94x8+9uv/1Iwz2F8zVK1FhFYfXhBiUPuW6qPWLuGMG95q9ovhFeWl1jdrGItajKx/h7C/9hBJBbfwZwVwutGke4FifVTcoyi+q+EWFHtfWvqgY+X/yKmg11ZN3+G+4XwTAjfh/CFjcUxiSWCoFNCmGjFOYZaguGWM9qid9KQF0L4jxA+ZIeBxGScbq2gSl0SDUERP4TwueUwKn/TMH/PhjCJT+JSxGpvtfp3aywVa2CZUNzviZL2PSeIIvJgCP8Zwu0ESk0y3MWKftvurWNZkf6bEGYn966T6KGxVsQAvuqxKy2sjZNN/aeJtbdVrElNJhasGhzCAyliEb7+K0Lsh0BU6AXW2nzEmm8tJxrGhBDGJcS6xy7uHsJi01IllqxCtDXOTbIe7W48A4TyGG290sf6zzuYPSGuMgqONhPc1XyguVZQa9O+TrXeRiDuPdwqsInVs7uRb6wddMtfGRjwnBV0TUKspkaCPiaNKUb3upYJBz/bAX9tjDRTjPp8dj3SGAPWMzGeb8yeZi5Ec0s/0nS539vILn5nwu9o1wfYvQ/biO1iHsKfKWK1tSaHGk4shP4jH4wnxIIlVycJ0sfZxFpmt5eZgWubEGuxabKZierqbZkXiVFZprCVifVAK2Xn5CJc+S3x6oIN3GWmZsCrIfjGSCiwp8wo8zeDz5qTxOMqMoVrmortlKguJ1YPI5bnhkL6IEl8cHlTuJGlP9mSDTRR1DOuTM9V0ODyppDvx0uTIh4xcoMv7XbHohSxvGtqOrGCmYwRCbHeNKE4nsvfDdEU/ptpqZBlCqPSXmqGIyQWpHhiEVJolnlvl1mHdUoRa3b5G4dYH0w0jdUmadEEu9H/9iqaWE2trI7lfSy+jZ+Tyu2cPMRqb+kHpVJilNnhZGoRxEIn/SN141l2sTRRwBk+lj881QJiHW/Dy4l1k3VJXdMEfzc/ozCxyGQJexHkIdYa1iV97fgic0oKYGR5Yg0xZrs1KUAsDNBbps82TE0NXG/aq4GxmVAN8YPrMUU476VW1WCejROL1v3CBmDJxX1Szwrzyjd2rqk3sG2ibKjYryG0s7rBuR2SxFeUJ9Y2ZjFd7/ZIRsK/mvaqY7cvSxEL//IO81lrOrEaGkucWM1t6JeaHzPMWlWh8/6cOac5iRWMmvPMF5lb0LNxgS6wvlySdMk8q9Uoc4z+2y5OtQT+tyi5Ef30h9Vnjj33+aiYYE1YYP23dsri/GL3LsxfjWPN1PLwcXPKeT/X8p9lkokd3Nx0pFfmuOTh+h+WZr49PUQO/WK3fxTCBimVMye5t51d6Z8UMTMhVmdrAsluLa+x+pfnWW1C8+qejmuTPOlUCuiACsO9dTWXvIX9dTDV2yHlfrVcrto2zHVjPatMMa1Y32qeRgO7WGHkifpZRdTPo5nW0vz1StC7883PQ1m+HMLHKb9eEKoE5oEOMpvbucaswAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAjLjSZNmkgIQvWiWbNmoaSkRIIQqhe9evUKM2bMaNGihWQhVBdatWpVWloaysrKZs6c2bNnz6ZNm0ooQlUAhdBVsApS/S856Z9QcCOqUQAAAABJRU5ErkJggg==", - "description": "Allows to send any RPC command using it's name and parameters to device. Useful for debug.", + "description": "Allows to send any RPC command using its name and parameters to device. Useful for debug.", "descriptor": { "type": "rpc", "sizeX": 9.5, @@ -28,7 +28,7 @@ "alias": "rpc_remote_shell", "name": "RPC remote shell", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAU6klEQVR42u2dCbSW0xrHtyZDOpVkyFCKIplCZpLMIVJIhsgYmSUakLniInOGyFRXaCn3ilws89Q1RrpRSq4k15Tx3N96Ht9e73m/4Xzn9EVH//8666z97u99997v3v/9PM8e3meH8vLyWbNmdevWrUGDBkEQFgNQqGvXrtOmTYNUAVatssoqqhShVGjcuDGkCsgq1YVQWnTv3j1IAwolR1lZmSpBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEARBEAShmujRo8fxGfAB0NZbb127du3465577hl/7d2796677tqkSZNUCsstt9wee+zRv3//c845p0uXLvXq1ftLVtQ222zDJ50PP/xwlZ7abbfdeOruu+8mXKtWLcKzZ89eJoj1/vvvl1fEe++9t9122/mvEyZMSP26aNGiQYMGxcfXXHPNl156KXnDBx980KpVq6Xh1aD7FVdcsdVWW4lYfxqx9t9//y233JJauPXWW7mcM2eOCx4n1imnnMKv1Oyxxx47b948Yg4//HCXVc888wyXt99+e7t27eDT8OHDuXzttdeWhlcbOnQohTnmmGNErD+NWC1btox6zWPQiZFYKMR4P5QiZuLEiYR32WUXwq+//npSLb711ltEbrDBBqmMDjnkENTluuuue+mllx533HEe2axZs/POO++mm24666yzVl11VY9s27Ytd3bo0GHvvfe+/PLLhw0btummmxJ/0EEHXXfddRdddFGbNm2SKcPpCy+8kET69evXqFEjj6QzPPXUU5Rk3LhxJ554Yry5c+fOJHjttdfisIDS5qyTpk2bnn322eR1/vnnR+kbiUV2Q4YMufLKK7fffvvkU+uss86AAQMoxhlnnBEdIIhYLWPMiy++WIBYO+64IzHPP/884csuu4wwJEgmiB2GQbbeeuulMpo8eTI3z5w5k/8kS8xOO+20cOFCLr/77jv+z507t3nz5sT37NnTpSb/f/75Z/5///33Dz74IIGffvrJ74/pQ5pffvmFyB9++MGbbf311yceLxcew81Tp071m2l1T8TTueOOO7IrZOONN/7qq6/IlxTQ+9y57777RmKRPsl6qX799VdeNhLom2++ie+CH4S11lpLxPqdWMsvv/zRRx9NfX322WcrrLBCNrHQj/fccw8xI0eO5HL06NGE4UExGTmxkCKICiRQ3bp1P/nkExpp22235VeUbGwAJ9Y777yDDKhTp86YMWO4/PTTTykkbXPjjTdy6XYeMT/++CMU3HDDDRlzICr46emnn86pCg844AAuKcbKBm7jMiV1AGqd+H322Ydw+/bto0h2Yi1YsGCHHXZA1CHSuKQ2+Im6osa+/fZbDAYu+/bty0+33HKLiFUBn3/++c4775w03j/88EPMpjfffJOunOyOLkXQUMUTy5VarHFvGNehX3755UcffRSJhcLynw4++GAu4ZNf0uRcjho1ijDCkjAKKCaCmKFjuG+LFLHQiVwicaNq5hJ+pMp52223EX/NNdegtYO5YXH1mrKxUJFcPvvss4QZCxPmQf8JAkGyt99+W8Qqv//+++lh1AXhPn36xF+TxEJFPvDAA5hE0YBwSz95f6XEwn7yS9RleRbQO5AjRSwkXJSRPtbj8s4774yq7cADD4y50PDEQIJsYiF4snOMyUbQZ/zO3377jVc+7bTTEOTZxIJ2XL7wwguEse2yU0Yzili/q0LsKmoTM8irMqeNlYSrA0aCyUgMWziE6V2YWK77nnzyyeMrokrEuuSSS1K6mASJwU7KJtarr77K5eDBg5PZRdmcAkqNQcaMGTN4BMFcmFgMFAg//vjjyZS9v4lYvxvv48eP5xJjpRhi8RSGM6qTYZTHIMzmz59PJBqkMLG22GILN6QwtjyGsd5KK62UrQoLEMvVGaZezB0dhBnkcyUXX3wxv8YR6M0335x8NS9D9kuRGmaWh+lgDC9c9hQgFtN+borFueWNNtrIjVQRq2UcumOjfPHFFw0bNqyUWIDpAB/rMRWJ/Jg+fTqXDOYrVYUxcWbCTj75ZB7/+uuv77rrrnzEuuGGG7KJReNhVCFlsW8QG2+88QY/MUfgd5500klcvvLKK6TPZevWraEdxj72EzeTO2+abbyj7t30ZjB4wQUXkPiUKVMKEys5NCEvSk5G8FjEqjDd4KMwWFIMsdBcTEH5rCkgMHDgwOSiUAFiMTSDED4pQBtPmjTJ7eXiieUNTMo0P5FIF3KPs1NIzXfffZd4+onHIFqcfICxAhTMnsrC/xg8YKLBb0PBrb322pUSi6foFf4UAvvRRx9d1qcbSgKah6nO6rlFRd1A68V0JweHaOmoVSNoS+YsXMMm5z9z3pzEiiuuSKn4X6ViIEF5Sv4WBUEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQBEEQajRWW2211Q18tF5z34KP63N6C8dvIK4W8rkkXXqaoLBbgKUIeE9Meuno1q1b0jtIErgbxUvH2LFjs/0TLWng1qyqmeKM/oyKwB0X8TgdjS4Fk8BLL6+G+4nFL+2pp57qOeLapLROHPB+iHekmkEsamGvvfZKsqdA0WndP55YSJH77ruvqk2OF5DtDHgsgpcE8CZagFg4JsElbpESa7PNNsOvWj7hQfPvt99+eN7CS2q2H8plhVgcNoGUwnsdlML3EH6t8M0SzDUyrqHw8LnJJpsUIBY340kRx3kcaeEqkurGGRoes6GsswHXyMhFYnBie+655x555JH+LK70qHpikiIzBTo9xSBT/A0hA9xFMbrssMMO8yzWWGONwi/IbXhBipcQCxempEa+HH3gL+UC5vTTT09KbvLi1XD/584BAQ7GOXoDf4W48sKnd20DL457N9xn7r777s5Lmt8fwYFR9BOGCyQcpfIu0R0Xmpf03TO5CzYq00vCiQf0dn7F1039+vVxC00WMLUmEYt6OcKADzs8++CtioqGZ9dffz31SNfEDVWUFiliIRXwMAtpIB8V5O+MxqH68KqN1zJ8l+HWB7/wONDCTSiOyKhx3C4iHqhoKh3/sNQXWWC65SweUoTCkGnHjh3JxUtywgkn0JBkQdZ4vI1eLYskFo+TLH3GXeLyOCnjAAx/a/G2QQY8DNIN3PkxHOJZxB7ulpCC7uO0U6dOI0aMaNGiBXfSRd2FH81PfeKhHjbAv2De/ahecuH18dq6+eabB/NFCBepc7jrPgfhEPdcddVVdFRS4+3oQpQW13b4hEZZU8IaQyzenw5BJeKok6KjdDBjkQRQwVUJdhWCPSex6FjutC4JqAkdk2GIhQCgRiBfMEeMtI13Tc+CtqEY+UpIa6WsH4gYpQhhXBBWiViuCsk36dYWqqWIRbsGO9CF3Osb6BJ+jhC+3bzA+OWma9FnIFz0pAWxEDa8NcTy8ztgCb4Fo4qIYYQWTEKrIgVj1vAsCnVA96Ndap4qRE/BIbogbm3pN+54DoGP1Dkgg9iKKWJRrQi2bDsg3o9LY3p2ili4vYNYAwwxiwL1lSIW6ubee++NJ1PAXXetXlViUbDoIrAAsRiIkTueBwkjh2hmagahEq1y0qHeeDUY5n7nYg2gpr26YFL0g0oi7gSV/zAPT+NIX47VSBKL42fiJXVIPdc8YiGWIQf1iDIi4L7UqVP6EKILswlxFSsxRSwqjjbGjSKNjeLzM05ICvXq3RGXk5xikpNYxLhHSc8CeZCvhKiDlMSCK+5/G6OELAoPJkpFLN4RHqD1EFrRxkfe+IEovEgU1ZFY9FXqB1WLsQUXsT65jUrAyEt2D+qnALHgMdqQ+5koQWTWGGJRoVTcUUcdRdERV5wP42YHYgyzABJQxd7qOIIfmwEaM2pS7qG1kHluJ8Gnq6++Go7Sg6lQr7hsYpEFcpEs0IyY0oU9MtK5aXXyRY84n8iOg27IAturqsZ7iliu7CK8BnJKLJqfSkD5YidhOVBjDEeQKIgxWMXr+DgRYo02EOlaDD5hXXCJuca7+G28C6+POUW/IkDPhI7JkmCEBTumgF/JsVevXjVJYhUAOqiYaRhqDZmRGqjzYE7/tinAp2rP9MDOP3KqFkvO+xhg3ADDfFDJi/P6xbgqpS+lbuPd3d6vtIbdg7XwFwSuvJFVHDSEAMYqQrDVmElwYSkHYgMrCssJ9bSUL/4IgiAIQj7k292QAnMKvpUjGlIEPCb7rHUh97gsrv8z4etzQgzFPYblWxYl3J5gUMMKHUNu4n098Q9DNXY35EO+RegUzjzzTBYEmTeK65gc6EoMk/XMdIg2lYMRL1MmTEcxvcvymU81cbAlc1EsesAqJlF8MoZ5F2rWl2hYySm8QldCVG93w2ISy8HEXmqBnIUKEasKxPIpGV8hZtYEYsW5YOQTM7/MGFHLvnpKSyPS8g22kWo8wiIGM/I8SEsEm6NnhpA0SZkJ/WCH2zJryrQygywmCb39OMiUhTMmuCGxz0nm3N1ACXmW1ArvbqDM3MYEKWtHrDG73M3e3RBsAp1ORUbx8FURqzTEYnMfY2k44ZPRND+NzUwgzca0MusnvpgTW6IAGIozZ830Oue2kQ5kxaZhpYimZVKe9YpDDz002Pw7EpGFDqawmRyCZzQ8y/isAbDgDy9p5pBndwMiE2aQGhRBN+WjOIIWEqDCfOuBT05m725ArSOVEWMsSTGN7p1HxCoNsahQ1A0bGXy9D0L42gWrEOxSokkwWrkt3+aWFFCXpBZ1JYQgKd/IAG9QsiGzsIPxhBBCC7OChFnt+whCZiHFw9m7G2A/fcATZMXNy5wNxCqM8d1m8fSv7N0NLANzgJ6nBl8RmSJWKVUhK1MYWK4vIFZyI0ewlWC0ZGzCwgMriBVPKA22Fktzxo0MvuHEiYUui8RCclASp6MLSB9/ZROLxGn+mGABbUjWnJ8Lb3jEz4DNXitkuwHT6DG15InDIlYJiIV4oNLptTmJFWyZ3Q9sRrOwwlpgP0KKWPCDFvKBJCtuvgUgm1hoNBaV/Sxd2o9Wd5Zn725Ai/k+J+QQVMi3YggnvCdwA6ON1NbkSCwCrGe7SGPVPCmViyEWiaNbfbldyG2800jsSsC4zkks31NAXbPGzhmn+RJE7MX1efSpR2Ih0aK0Lqv03q7ZxAp2QjPr/1h1qObkMn5qdwNtj4zhTkpCTL7VFcQVth2kQaHDRTfFsonF4ySCsqYzYPOhhYlEXyc3GiDtKE8yxk3AYNs+2brDUEBcWlwi+rCuqqBno+Aq3fJAMxe5kl/M1oDitx5Au2pPlSFTi9nKIQiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAjCHw0+bGhe9afY+s4++RI6reJDgNXUGNXDwBDGVvzbZslkdCGO3Yu+uUsIc6qexYYhlIfQq3RlfiGEKUuy8vki5by/KrFobDzRDgthoQX4W7ta6eA/+qqCNzxn9yxRYvEVGN8Erli6ylmvWoKzeJwSwmN/bbmFC+LPE5dlfP8UwoshjA+hfSbyXpNng0LAJT4+u93JAd59/xnCEyE8mxEVfMHDh+v/CmEM7hUt5hhLZ1EIL1sKl+UvBt80/iOERziFIEGsjiE8alkckUmflOPHfnzAtbUF7sxI3FaJBPe3lqMwh2Vi+AR2pJWWjwMbFewnnlqfRCR+IyZYD8EFeYHzMEZzOIC9yMOJwhwUwiSLPMQuN7LEZ4YwN5NR02WBWIhoTurYzEjwn0xkJ5yqh/Au7pNNgLc12fYdrkRCaBHCghDch8vQECbbryeHMMvkRxvj4schXGmBDnnKwJ3zjNAdjCVOLJ79MoS9LHKmESVYgw21QGsrgDvH3cUS/41jDTIJotO/5gt6zi8J4T2jFHg6hBGW7ChLJx82sNQeN+I6lre8jrT3PdeqKB/+F8KNlvsk6xKuFr6wBOkkn9nrNLLLu0J4yQKdSypol15igbWsqY4zqyX6K+ofwttmIzs6WCXWsZiPrb7Ah0Yg16q0xE6Zm9+pTBW2t7z8Q8H9MsSCwa9kUnvMCBeMK+9bYICxPIkksSjGAxV/Xd2y6G2p8YHsz3wSWLBIoxLEqmPkuNsEZ+uCT/3PCAT4WPsDC1zHh7KZX5PhwcuaKoRPM0K4wuRHbGwn1uSKNg1q67UQnjch740019qjf+avTdHE2t5oUauijYW3rqmJ1A62yBXMImxrWe+bn1gjTSsl0cpeZ2AiwfpFE8vF2FATQgjCfkUQC6033QK4G7gp8+vwEB5cZolF3V1sgd0KEqupaZbWNiyPeMjoGExD9beO7vh3ZcQitZ9C2N3C52SI1cU0YOOMrdY60eSjrMx18xPrCGvXMpO4DCyaWWHmmGZ0Jdu7smpJEquh9bdaGbr/vSrEwlB704pR13TfaZk7z1/WiNXNRAI66BKrkQkWuchI5n8vW8zKpvh+MANrtlkeweytV01ufWvdtFaClIsSz+YEFf2jpfm3hPGOSfRVCP8N4S2TGdGOLjcjOmJBonjl1oS1TXUutJJMzHSPTlbU2RZ/fP6SDK+Y2gnWeSabNkQLf2LjjOKJVdcGHPMypno0LbYwk8uzaLHsTFFWOk7pY0K+sf0hWn5JyKcmRrtqoCzXg/UqCsXFf5E1E61b1eI1S1iZVUL9zDhDqAQ9rQsOs/79nM0pCEJpgADfx8RVO9WFIAiCIAhC9dDLJpNSYDZoiKpGWByMqDh15Lh2Ce8tEWoqtrWZApZlOJB+3UxkT9u2MMEm34Mto46zSdTpmdX4JNueUCUKKSxndOlrGzn62e6OYGvAM2x9fg+b9W5jM4qdM3tafDU+oq8xUhDSmGnSqHdik9MYW/LzPQVTbSOA445cqhCRdroqUchGc9s3/JAttw22mEdMD8YtAB0LEquR7bQUhApgKe3EzBofewqeskB/s8fr2TruWZktcsE2+mUT63B7UBAqYBWz0OfbHstPM7tWVrJtIfNtC8DkxBbevS3GV+MjppiVpqO3hRyob1tGU0fSsMtg1SKerV3dnQKCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCIAiCUAwaNJAzeqHEaNiwYejatasqQigtevToEaZNm9a4cWPVhVAqNGnSZPbs2aG8vHzWrFndu3cvKytTpQiLAyiErIJVkOr/sUwGfvJ+Tp4AAAAASUVORK5CYII=", - "description": "Allows to send emulate remote shell. Requires custom implementation on the target device to work properly.", + "description": "Allows to emulate remote shell. Requires custom implementation on the target device to work properly.", "descriptor": { "type": "rpc", "sizeX": 9.5, diff --git a/application/src/main/data/json/system/widget_bundles/digital_gauges.json b/application/src/main/data/json/system/widget_bundles/digital_gauges.json index 755ac603b0..9455ac4100 100644 --- a/application/src/main/data/json/system/widget_bundles/digital_gauges.json +++ b/application/src/main/data/json/system/widget_bundles/digital_gauges.json @@ -100,7 +100,7 @@ "alias": "lcd_bar_gauge", "name": "LCD bar gauge", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB5lBMVEVERERFRUVGRkZHR0ZHR0dISEhJSUhJSUlKSklKSkpLS0pLS0tMTEtMTExNTUxNTU1OTk1PT05RUVBSUlFTU1JUVFNVVVNWVlVXV1ZYWFZYWFdZWVhaWllbW1lbW1pcXFpdXVtdXVxeXlxfX11gYF5gYF9hYV9iYmBjY2FkZGFkZGJlZWNmZmNmZmRnZ2RnZ2VoaGVpaWdra2hsbGlsbGptbWpubmtubmxwcG1xcW5zc29zc3B0dHF1dXJ2dnJ3d3N3d3R4eHR4eHV5eXV6enZ7e3d8fHh9fXl9fXp+fnp/f3uAgHyBgXyBgX2Cgn6Dg3+EhH+EhICFhYCHh4KHh4OJiYSJiYWKioWLi4aMjIeNjYiOjomPj4qQkIqQkIuRkYySkoySko2Tk42Tk46UlI+VlY+VlZCXl5GYmJKYmJOZmZOampSampWbm5WcnJadnZeenpifn5mgoJqhoZqhoZuiopyjo5ykpJ2kpJ6lpZ6mpp+mpqCnp6CoqKGpqaKpqaOqqqOrq6SsrKWtraWtraaurqevr6iwsKmxsamxsaqysqqzs6uzs6y0tKy0tK21ta21ta62tq63t6+3t7C4uLC5ubG6urK+vra+vre/v7e/v7jQ0MrQ0MvR0cz09PP39/b39/f///+daHfNAAAAAWJLR0ShKdSONgAABFpJREFUeNrt3f9TVFUYx/HnriCSmF9ADUqyFqUFTTuRX5KCyoOKxopiwBpQKrG2C0HSjZBFJRfUxNqFfZMpIv9pP8jaMlM/5IyxZ3uen/acnbkzrzn389xzf7lHlpceL+B4LTx6sixLDymAergkjymIWpSFwoAsCAVSClGIQhSiEIUo5CVArvu+7/t+8rafLZjq+SblHGSbiIhIuFlWyktFi1/bunPGMUgmUBUOh8Ph0UQ8Ho/H4/2BenY08mCHhenGX92BTEvjqvE5iT2QAThaB4eDDq2IL2dyh3PbqjJprx8Ov0ts3ZhDkAG5ODXkZ7LDixKB4Dv3xjae/W27dSkjnVIuIhXDK4mp2pqGRJVIw/yJipRLkDap7ug2suEWAFE5DcDUPa4Hom6135vzwHFpBmDv+jvZbrangZu90TnHnux3pBrgmhzLznSXTfcVVW7a9cCxLUrJdgDjTayMZzZ9wcYWZja3OQLJHPwE4Bd5C5j09mfnDwe5JSNwsMGVFakrvgGExQKNMrgyG183RlKGYf8hVyBDgfKOy03eliRMF7+enf3yNLDTpH4o7nYmI1crRby9CeCkXFz1z2i5yNGMQ2G/P373HwI0eVdfrBSiEIUoRCEKUYhCFKIQUk3GmI9TwKgxxlxyEZI+ApyJGGMi7UBDmzHN9oaDkL5XgOY6oNYCRVcgIS5Cep5DaixQdBkmFLKWNVMLtAwC/a1A3SzMv+8ihCQwvfpX+p6DkOk3gBN9QO+nQDAJ6f3j/z3Ef/F6doErAcDuAeqbgLJemBKFrCFkIADYWuBAE1DWB7e9cQczkvkMuBAJhUKR80BTSyj0QceEq3utTNha2zYPTFhr7VXd/a5hRhSiEH1DVIhmRCHatRSiEA27QjQjCtGupRCFaNgVsgYQefFSiEI07ArRjChEu5aGXSEadoVoRhSiXUvDrhANu0I0IwrRrqVhV4iG/V9BuowxDZPuQ+LhY8aca3Q/IzGZh/bKAoGEFZI/GYmdBG4edB/y7AtcSfchw0eARI37GRmW+9BRoZD8gXiz0FkAkNEOEwq1WvchXLHW2lu6jVeIQhSiEIUoRCEK+btK2xGgM0K0FfjqHMN2ACBiE3R3MW5t68A8TNqfv7bWWmt/siPA55F8g8zKeaBmH7YMOFRJu+zMwGyJDBCqpV/q60uDs8Rk9IIxXpUxyeo34Vvpz39IiTcCkdIs5DpTG5uIySggbUBMrrJndyb/IZv3fQg1R/6C8NGWXAi1we9kiPyDiIhIDmTD5dLUDS+aAzkrqVzINa+8jjyEHPd9f1duRtKv9pzanciBnFy3akV4T/x8hKzcWm2BDBwI0i5zzbUVnWM5kLeDqyFnJJPHkO+9U7ej61tpl7kfZf3955BLQ0e9QZcgdG2RosY07TJH9SGeQ6S4ZhAHILl19+V9vFu3KApRiEIUohCF/K8gBXJA8O/yqDAgi/KkIA7R/uOpLC8tun+s+eLT5T8Bm8H0V8ljg20AAAAASUVORK5CYII=", - "description": "Preconfigured gauge to display any value reading as an bar. Allows to configure value range, gradient colors and other settings.", + "description": "Preconfigured gauge to display any value reading as a bar. Allows to configure value range, gradient colors and other settings.", "descriptor": { "type": "latest", "sizeX": 2, diff --git a/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json index 410e70714b..74260d3021 100644 --- a/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/entity_admin_widgets.json @@ -10,7 +10,7 @@ "alias": "device_admin_table", "name": "Device admin table", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAmQSURBVHja7d39V1NHGsBx/7Lgafd47FH2BhAEDIFoyIL1pb5gC4upcqwW0aJUDNpSGmFXAxVQV+silUUsqQVsFvAFRF6EKEFISSAvJDf3uz8ELOtaCZCzKp35AThzZ+7cz5lnbubkCbmrCI49GXzHy5OxAKuCwy6Zd7zIruHgqjEXK6C4xlY9kVcCRH6yapAVUQYFREAEREAEREDeGsiMbWpR7bsd4d9BmyvKkOajR4+e7VBe0bC8NoKzjUrdL9WUXXlde03d7E5JalvENdvtC0Mqky9aipIPzfxvw5obS4Kcb3ppzgxty4aYTBFAMgBHWnl4I/aK9sG5Tdr8i3tRNwcJACH5Fd3wSy3z+mjqwkdmIXKUIdQm+cC2Q9JVK+60OxDKrqPgDHhLEuNyHoFSm6beYQv3CFVqpM03gP6d0haL1M2Vj6rTpT3204lx+U7Ir8CfemW3pGkMz2uqlLQXrFuljaUB0Hz5cXzKxVnIqDEu9YQ/qpBe6QH98RbHrQ2tFByDh+pn5JbAoexux8k0F9+l3nF8lRJenxc0tsk6dR/BzE96H+VL3dRK5SO29KSSYVt6Kewpwydl3Bn5coMLYGpIuvKcXnXdZGeKBTSpt0fOS7/gktrw6j+339OXRxUyIVkpygNOGmlOnqFiH+SWMCLdheC29mBqHYTSw8v43kNQkq9wW+2AEamb2lQFvt4ow5nds5BGcEhdv4XW01ag6FPQXADyDuOS2ria4ocbScrr17nJZDLt328ymUz2CCDD0l0MO0pLS/ca8CdbybwGuSX8KIVvrk+kQ6Wlpelfhbt0nS4wJtRi0c0u9to04LweOJc9C7n923IOrxFH5WGjLm92sX+7HZfUxgltaWnpIWkimhCr9IwtH5vNZrMFTh5/HO+C3BJapXAE90snzGaz+UcAvt948a4tpZaqrMghT1JK7tgOzEEqs3BJbRzLNJvNZrMriqGlHNgN+4/OVv07peIQkFvCgPQYaBzxq+fdU3eYgbRaGjd4IoZUbgdO5oGmFjhsxCW1cX5LKLq3X1/P0cReuKFuJXS+HkKbE1rCkNCHB2doVj/hcKadqc97AdjzV3+wRqpmOvW0HDyzICQUXw+WFDu21H2gyXZgi2vEJbUxGP9tCOsXSpQgkiSpDwwAijkuXWN4DJhTZsIQhrITdUmN4MpX6xMLvOFdhmZD4sHMUmhJTNz4xYIQiuI+YmqXOjnzYAZoDqdskg6Hwkdvpabokm9FaUamRkcdgdm/PQ8ehQD8EwATLiDU2+0B4Hnn6FwX770RxekEXJ1jyugM0w5gagyYeg4TLkKjfsI/AEIPBiDU1xvwjCo4fFOdA8wdDfTd80driyJ2vwIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiIAIiICsOoti94LMv8VwTdrvdbve9DRBZtQ9aVUv8lOP+dWti1q2zvh0Q1S1aVTKBniGYdD7vkX33fwXsDwKRnO37D2DCiWL3jXke2QFv1/M3BNmZ4GlVyfbkTPVJTiVo123Xpq59SllsmnY6Qsi3qTyIGdUlad+rZkC9+YPrbwbSrD/RqpLv/YObf1JOZSntql4l4dJgzJCSWRMhxB4zUL4V3RkurQ19XMgP8W8GcrvnvXKVPJmfnqqST+XQq5okw/JDTHLymuIIIWSd21KLzsKAanxDbHKCyvtmIJx6XyWXfRjqmAe5u/qZ2+mLFPJd8monOgttMTMGk9vtVN4QxJuokqvXfZ2hmnwBkQ3Z5oy2SCHO1btBt6Ei7VMa1pwp3PsmQitUNQL3qhT5ytnOqomOJpxVfq4+wFd7OpJ0K/3VANqroDN9Y/FD51ffOd/VV3Znxdop0Fne+S3K45w24HSr2GsJiIAIiIAIiIAIiIAIiIAIyIKQoRVRRGgJiIAIiIAIiIAIiID8YSG2c6/4r6uh4uLi4qF3CmLLMhS9JOkxvCg9C0OUQJSuZHnfS/R8PM9gOPdS3cUX5flrIZdSwKLN3OcH2N+mrAd7Rr81Qa/LebrAuO5Yvebg+Pya5uPLcXRkN4/nZdleqrUWzxbra0PLkRHL8KYgB64C5FiVGJwaKy25UJO7wMCu9XBNJ4N3fiJXdgJ4/It3ZBn+0jz+soPLc5F1+bWQT6yxTPZB4fU5iDfzKrTkQm96BBB2dFChzzzri/dz1tJQSEdajn6MM4Yt5sU7DIat4ywNcuOYJxbgsd43B9mjV6Alu71l298jgRy/1JMphzYPH7mFdryhUEke5NrZzq2KnOZYZFwZDIasn1ka5FfdtCcWmNA9ZhaiMu2wQEtK2d4CIoF8du1iwq5d6tsdBQO7aSh0qQH+lrhr15/bo+KIDFKXpt+8OgtPVjt0/wI7O5QYJhK6aMnFFT8WASSQNHj9iNvtDoa0Z6/TUCivD+F31h93u91yVByRrhE8sQR3lvX0DLSnP/4pzqXEwN2kyZZcqDqyEGRtj3XvSZyJbcPFbsrWe2goxFg1dqR+LLFj6LgnKo6IITOncBYVFRVV0Gg80otyDLjW+KgO/CcW+FSJt6iopBUYOJrfBMM1cP863nLjZegrzL+9yHWe3cGyIG9DOfb78/HOQX5vPqBxDtL49kNqiot/14HvXPiFvdIntvECIiACIiACIiACIiACsmiIyLOL0BIQAREQAREQAREQAXmnISLPfmcahupvht8fnrx63QNg/yWyYYNNTa3//QXKo13LcowvPc+u7ee6tvqMdgpwbqquzAiCvHlnZOO61lSdSrk5v+bhteU4OrYuPc+u7Q/EPoPj1UBXPWT1QVV+pJD1MB43iedSvdt7Awa6Rmwot853w1T9Jc+iHdnLyLNr+3sygEA4tkL3N3kY2tq5CAif/hDQX6jRyRoHBS0NhZiMNzNbfbqaC4bQoh3LyLNr++9uD0/g5SbIi/+G0M7ersVATtT9K9/t3ttlrg5sDDYUyut9DDT/s8Dt3ta7eMeS8+xo+4e0gNNhvdwEyPr7dduazmnbI4fkNddojEZj99Ot1uM0FLoSAKq0RqOxNyqOiCFy4iB8Vg9YayDv5+aqqi+Sv48YMqCetuaAB3bs7aShkDgXfT825YMnOo6IIfykKTu0fQZwpplOZAeAiEPr/aL9qR2Ecg6WbXZzeaNCQyFXsiv0D+Wdn5Xp/6959i4v/GrtDq9L388dMsB0T2TDy+3tfTIQ6mr1gLcfJgZhxDoJcmerd5EOkWd/uyAizy628QIiIAIiIALyB4esmAcEr4xHNk+OrQqsjIdoy6tWxmPNZf4DJqTD+Gup8cgAAAAASUVORK5CYII=", - "description": "Customized entity table widget with preconfigured actions to create update and delete devices.", + "description": "Customized entity table widget with preconfigured actions to create, update and delete devices.", "descriptor": { "type": "latest", "sizeX": 7.5, @@ -28,7 +28,7 @@ "alias": "asset_admin_table", "name": "Asset admin table", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAAAAABslHx1AAAAAmJLR0QA/4ePzL8AAAm6SURBVHja7d3pV1PXGsBh/7LobW+txes9YYhiSsAoRKKtA63YWsHSWhwQcQATB4oo9QKptnWgqFhFMbEFLBVEoggyRMIUEwImJif53Q8BynVd5YDpUuneH5KwszmbZ+Xd+7xrveSceQQHuh+95a17IMC8YI9H5i1vsqcnOG/AwxxonoF53fJcgMjd8x4xJ9ojAREQAREQARGQNwDS2Tmj4f13J+Zv/8sgNftCszjON7nPdbRk+18yvHzV+Iv87JnP5XAogYRSpdsvPUz2aUWQ9oLA/3ac3BktiMmkBNKUkJM3/jL43HMku1x7fHJsMDyOD01AApMP4y08mZIWZUY6ghOQ4BSIHH1I3s6GBC8QKl6uXlMPoZLlaqMNaFonrbTQoFVrtJGgaf1E0uwcgdBhjSZvSy4B7amM2OXVDQYp5RrUaYMc2XFME/fVGIBXmxCrbeLJ7gT1xjYo15fo1FuHI5BwZVLshuYoQ7yaW6GUc8BpXcvg0YR+zmrvDJXE9dIeV9lfG3/L71xd5AwDDC89Otxu2AdnNDW959S5BCTj3Z6C2PSmnn0aL9ekIAfVR3vrNRUAIWfeRqefzzO7BnelhCiXCrub0r6KQP6jq+8/pB2NLuRCUpDijUD+p2Gene7lwIYw8ukedm8FCnL+DC1XnR/KDKAvBnJyCUg10C9Vg0O6G4Gsm7J6ijKBm33QJnVTrguBTd1PfjbPEs+DrL2oaJ2bTCZTVpbJZDI5Xg7JMMND6SG0JRnLOoEHuvSTD4G09UVFRRnGKWvEU7E7e7WeMelm5M8NSNfAK9nAJTVGIJunLOeiTCB4Zd+2TKk9stiHpSbys3ko5RYVFemORxXSKekMBoP6COC5lK3O8oO3Jlez5Sn6z0pLS0sr/oS4V223Nh3Q45HqlUN2Gi81VU9Ankj15Gdjlw6WlpaW2qIaWkdW19XV1RV9+AxbL/Sqr/BrDzjjqvliYi+bgFzWhKBCD8vPANsVQZ5ITdAhtVOuB+5I3eRn45Xqor79BnTlAMPqG2ze4qNTfYutm5/SE1tHtdpG6NQ5+CQ/MrZW3UpHmg6O6HtpS5wWUpwawqsuC3tyJTvlUnV4bMuGMPnZkGPsx5P7MJqQOrUzctLbRq9xWVrs/hCOj5amxe4NES6JTUkydMIZSecBkHOk5Um71WN4MyTd6sxpIS1x2t/4IXZpfJF0g3JD5oexOnvk3Sefq1M1uf5oQkb6xzfhfgg9bB4ECHfeifSOtraHADraxnOYrja/3xmE0P0HAc8Twk4fhJz+yIPPGcYzDLhdE/lVyxgMt7hwjuIdDrU3+ybfHRifIpopikjjBURABERABERABERABERABERABERABERABERABERA5hgk7HgKPscsjzXscDgcDt+bAJFVmWBVzfK/HLMWL5y/eLHtzYCormNVyQTsXeB2DdplX6sbcNwLKDnazx/AsIuwwzcw9sABPI2UJl4DZH38mFUlOxLT1PspjE9e/HGydtFjzEt0yaMKIce13Jvv1C9NfqeSTvXKD6pfD6Q2dZ9VJd89zy//DBemhxtU98PxPz2a3xVOsyiEOOZ3Fq9Bf5ifFoU27+JK3OuB1NnfKVbJ7q0rtCq5cBP3VW5WVFyZn5i4sEAhhPQTq86gr6BTNZSwJDFe9fT1QCh8VyWb14Yap0BuL+gbcfmUQr5PXOBCX0H9/GcG08iIK/yaIE81Krly8bEVKvckRDYYS1fUK4W4FmSAPqFEt41LCw/v+vR1hFaorBfuloXlc0fulA03XsVV5ufCPXxnDl1XcrSOSoDkC6A3fVvhhztHv3e9rWd2V8kiL+gr3voU5eGmeuCQVeRaAiIgAiIgAiIgAiIgAiIgAjItpGtONBFaAiIgAiIgAiIgAiIgf1tI04n/8x2yroKCgoKutwrSlG7Ie05iN0w2+0wgoQCAPPPKuwwXALgYZLZXJxoc2mIwnHiu7/RkG3wx5McckNP+OLUsNWX7GMCJlNStQU6sSN2h4LIDI0tSV6ZPXAPh0EXeA0A9Wps/O0ejsXZoS3rTc722gvFme0loBZbZqf6UkmOEdh8FHuhlMn/pSpbZ0Dj9xJ5/gTX9z5/fAzyoIwX60SAQmEmFtzHdsLp26HkHZyci6+zL1sjlTfLydkqOwc9fAa4OyKkFyFAIaTSwt4qB5ey9wHuMGtdsWDh6aRe6L9fF27mc8NH6PTNwGAxrhpgdJJz2zQ4o+bqh5sPxT+5eehDMxh0Kisyef2xct6RtKuTkATzvj17ahe5XKg/JUh+lexTHlcFgSP+NWUJofHcASozm1O8oS10LzpQeYKAx5YECyOIR9xV9eApkmxXUo5d2oeujOu+xDn7e84oOpRCXBJQcw64NArhT70BfA5grlYUW74/uOz8J2VkzFeKOVwx5sWOmEHIsgN943G7v6ltm717VqgCyyN5avBrLth7zOMRq6KxaMAlhraVj455XdCiFjB0CrDfAcRhw5uXl5ZXRvP1LJdXmp3l5e06N4DNlXTjG5WYKoCrLctTfWs23blouMrxn+4F9Ste58QXbi0LIX9rO9PkyLykYt+fFn8ebAfn9izVlYWUQ4wu3+5oJSM2bn2tZCgpefNrynYic2E/6RBovIAIiIAIiIAIiIAIiIDOGiDq7CC0BERABERABERABEZC3GiLq7DfdIF9n6OrVOieEb/1oB5CvKazGtp+96o+iY2jWdXZic8Abg01bVqytZtfX55OvAeWqx4omtqRaDiV7o+ZoXDPrOjuxmht4Y7BtgpaVwVSZmm/AYUx8zLOqyq5pJh6RPGCqocp+EWdllQ/H71Dj62ir/sE9G4fxFerssdYE7zjEZgQoshDOuJf0mOyD1ZppLgbR/HHkOSazujfxXGmaXLMTEocs8T+a9PJsHK9QZ48dPLrTG4NNnf2ZtgW4vUbm3EGSHmOsCnRM8/3h65/jNJttxIxReAqyro9DTGBsnpVj9nX22MFA8tUYbEb7D+uBjhQnQ1LV1fgzo/37k/c+e/ncf6Qz2rDbTEyAnFowVY5DzJBzPVoOxRBa4mKwbSKcbqVf3wm9ZWVlS0zDFuQtV18++bN/d8J3ZmICHP8WMqzXc5DjhiwHIeV+tBzKIRRG1sjvybI+w2w2y0DSY3JzDmt7p5neurRwv+Y3YgJ49IVfb5JdcabNC4cs8aatmVFzKIT8EQD/bdz3gaaxxoaGhoYQ0OwnbL85/dbjsd4agdsh8Dc0hWDI2tcSsJhbG+WZO968OrvFPNPfeEPr7B13ZwERdXaRxguIgAiIgAjI3xwyZ24QPDdu2ewemBeYGzfRlufNjduay/wXtgu0d9kLWo8AAAAASUVORK5CYII=", - "description": "Customized entity table widget with preconfigured actions to create update and delete assets.", + "description": "Customized entity table widget with preconfigured actions to create, update and delete assets.", "descriptor": { "type": "latest", "sizeX": 7.5, diff --git a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json index 313dd52323..bec97fe3cf 100644 --- a/application/src/main/data/json/system/widget_bundles/gateway_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/gateway_widgets.json @@ -10,7 +10,7 @@ "alias": "gateway_configuration", "name": "Gateway Configuration", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAR50lEQVR42u2dh3MURxaH9SedfXcuXwJjsg1lwMacoeBIJicTTDIgEBlEEDknk7NBJoPIGYQAkRFCRJEFGElg733sM13D7Gq1rNZY4fcrFdXTM9PT0/3t69c9w7yEQCDw8uXL3Nzc69evX5OkEgiEAKmwsBCoEqAqJyfnyZMnr169CkhSCQRCgAROQJUAYmyoUaR4CZyAKgHzJVslxdduAVUCQ6PaQoqvgEpgSX8qWL9JUlBxA8uK+1WSgooGr2LAcki9CuplUIVShZT1vpFQLF7Fg2VUUWJBQUF+fv6LN/pFqjBynQ4AYGB4GVuxgOWoglZDinSUQ6xU/kTXA4DhBRKR2SoeLPCkILAlocaVwAAYQIJELGA5c4Xpe/78OQupalPJdOfOHZAAjAhGqxiwzFyxQm+PFSUJAQNIRDZaxYPFmPrw4UO1puQVSABG7GDBpsCSigILPEoE1oMHD9SUklcgUVKwmAIILCkULMAQWJLAkgSWJLAEliSwJIElCSyBJQksSWBJAuv9gXXy5MmfihAPxnnpggT/X9Z31pYtW9LT010J27ZtszRHcvy9e/e8B1MxMvn/a97M06dPL168eN68edu3b+cAX/m3b9/mlLlz565bt85bGme56u3cuTMzM5N7951LnX/++WfOXbt2bVEvEVEO1/Vl3rp1yxW+efPmU6dOeSt26dIl27Vx48aDBw+WlceyfxpY06dP/+8bVa5c+bPPPnOb2dnZN2/erFSpEq3sO6tBgwajRo2y9MiRI7/66itLcyTH9+rVy3swbwWRSWfbJlX94YcfuFbjxo27dOlSp06devXq0dPueGCqWrUqFejWrVvDhg2rV6/uzp08eTInWvWoA+nmzZt76Tl69Ojnn39erVq1li1b1q5du0qVKqtXrw5tsSZNmnAud+fN37VrF/X88ssvKZw74ty6deu63ww/A/Y2atSIvZ8GlZyczKtOAqt40R8zZ8705sQGlu8UH1jTpk2jVyDAGRjwokDrpBs3bnzyySfgbq3AG2opKSnkmNUErFq1armSQZ++HzJkiG3evXu3Ro0aAwYM4LErmzQLdotLHzt2zGeuoKp+/fqzZ88OBevy5cu2+fTp0z59+oCXgWtgWcnUCqPFpfmFCKz3B1arVq1odDdYeMGCnpo1aw4fPtxbGiMaZsa6nwGOg69cueL2Ug57GYNCwUJ0bbNmzZw9A6zHjx97G4e9Xbt29Z5ChTt06LBgwQKMk/eTBT6wENUgh3wfWCbYIuf48eMxNzgtE8OuigsW3gkoDBw4MBSss2fPkk5LSyuqDvQr5mTSpElhv1LhA4t3IxmY+vXrZ5swFGpCMFoYPDdm0UqQzWiLG8eFDh8+HAGsrKwscnAow4JFy/P7GTduXGytvXXr1t69e584cSJ0V0ZGBrtSU1PLOViDBg368W3RuxHAYjgDIxK7d+/2gbVv3z7S4BWhGqBAlzdt2pQLUZQPLNyv6UExROKltWjRAkRsL04VB/hKs5q4Y9ikBPtoT+fOnbm1CEMhvYsJNNMbChZq166dz6GMXlyOc0PZMqrYBXnlHCwswXdvi76JDBa16tGjB35MXl6eFyxsFWlmWHb8kiVLBr4RI4t3cBw2bBidysHdu3d3M0q4wemx4/v27YsrDX/nz5+3vezyuU0OF9d0nTp1clZtw4YNDjJ3JDfbunVr/mUX9XcmLSxY1I0CY27wULbiS1V5GwrNzGAkGHRGjBjhBQuPhLTz3Dke28PAR+aKFSt8V+GOGIaghxHHbs03FDJcMg4yc7Rx84svvhgzZoyvEBYdKNysDjMAbOHChQsvBEVl2Fy1apUXrDlz5uDPrVmzBrCwna6csGCBdQn9d8cWlcGFiC9V5RMsRPewST85sJhh0ZfLly/3rTw5sC5evIi58t05e637Q5137xWZXbZv395X1fHjx7OiYctd3F2lEDHVCDsUgjsm0zVpKFjPnj0DPjAtYd+DkbEVd6rKLVhUjJEC18e73IBng43xrgB5waI07JO3/x49ehQBLHPIbCXTjNOZM2fcXvwkrm5VxaoxDWTxyXu6+XzQHAoWs0vOHTt2bFFgTZgwgTU2572V3G7FnapyCxbCPaL1vWDRc1gClgBYT7p///65c+cwKs7Tv3r1Kntx4xgXWHPHetHcDKm2mGkLCjaQcQDePZtDhw51I2ObNm2oG6vqLHEBTdu2bcHU1u5tdcC7EmuncACIhJ0VsiTBjJK5oQPrdFDM1/Dw2LVp06Z4EbA1KK1jRQuWeehesOzxCGBhaWwwwlN2C9w2GgIW3WZ7Gd1waZ3z7oYwVllZQMdiea0Ixo/6MEJxAOUzgXDPo1g4ZW0itHGhirGSBgwFiyblqQAjlAPLisWppzQfo1ogLS0CCMDFU4mwl5WqGErm/2cyQvkc7Yopvd0gCSxJYEkCS2BJAksSWJLAEliSwJIEliSwBJYksCSBJQksgSUJLElgSQJLYEkCSxJYksASWJLAkgSWJLAEliSwJIElCSyBJQksSWBJAktgSQJLEliSwBJYksCSBJYksASWJLAkgSUJrD8WLL6MzWeD+dj/3r17SatLBFYcwOJr2Hz1umfPnnwvny+YE3Vtx44d0ZzIZ9ktOJYksPziE/58aJ+wDnZh/iXqGjneoIFFiQ+7EwFL/Sewwoiv8vONfG98QAok+BGRcFwOwR2IAEAoAOyT+zI7VBHbDQRJQKc7l5CWy5YtI2qDiyRABIADBw640ghI5A1MQkgwdzoBTrgEF+JypC1zz549Lq5TIBi8mSu6qF0mQgeQSTAmImktXbqUyF6+AZ1LEDeFwCoE9HKRpIntw1mEKCOMBWdRZ+6Ovfv37w9bCO1AJoUcOnSIr8kLrEgi9HLkSEOEYGBwZJQkxEj//v0ZMS2K6YwZMwibC1gk6AmjCtQokBhahK7AmBGYJBCMJtKxY0cqaRUmYIQLFEhHUoJxg40k0BIBwwgDkZiYSNoCjcIZkSxcR0It8dx8kTKJ20u8k6SkpMGDB8+aNYtKEt7C/QaghAqQT3hzgvlMnTrVWpnqcRYRvKg2MYU5hutOnDiRcjiYaCtc1xHMj4HQQNwXd0c+DRIa6lxg/S56i5blVxjhGOJgEVXGcUDjuiAovqGQIDbsdWFn6RsLf8oPnatYYEFiitC1wAQKbGJgKAF7Q3r+/PlTpkyx++eOgMPihAEcpxujCC8QxH2VNLDocjvdNg13zBjVOHLkiB1Jgl0WXMnActYUW8umqwMnUjeiCQeCQYf5PbjrEiOIXa5MgeUXMUVoSgcKQZFmvJE3+o1dCEMFInSSa18fWAR75kS3ad3GQEMaE4XrRoLxiHCVRLwxrx9bEhpkECPBhSiNPrZLYykteiA9SpkWrysUrPT0dJeDwfP9YPgV0UTEd+VIG4uthu6XwF4vZwijZYHHKNl7JMJa++LjCay3TmeYc0EomeWtDur7779nRLBMeCJ4E4fRVVDF/LEosBih2oTIqr1y5UpgCgTDg2MYoMrCPDFyOayxW5hGxiNMGmaJhIGFWAdhhIUMEniEoW0UChbHW4hyhLEEU8wkVofTOdJCLPnAwqtj0xsomoOtcZg4h94ao6fAKlJ0LYHdfJn0ugNr9OjRMOHcWAJfFQUW2DH8XXtbFmgJ35kjGdQAlPBJeM1sEoaJznY+PtFWMWwuAiBOjAPLuIEDvB8YDb2LCGDRLAzQ/FqsJvj+MYDF8p5ZSu+t2WgusMKLIY/eJcBfWLAomb143+5ytLUDC8PjBYsJJgOE9wbMYbdhCIMHoLjJbigh3adPH3cw7vb69evdJmGhHViBYPw6UHMmMHqwmBmwy80xiT8YA1jm53nLd7cmsMKLc+ljC0vMqIcVASMGo0WLFjlnmT7m6vg3GCTa1wVLxjs2r5xYpm5JjDkm5g0/CSPBJMtNnRg4OJi1A9vEAWLT64YzH2SgxLvHhjExZK+zmja1JAd7GfYuIoBFtakVzhwA4VphFDnS4ntHDxbtjOXGQ6AEhmxMF81CiwmsSHLOjbkOTMdYL3W/SHqaRiQf74qZP+2Lv+JOBAV2OQoxD7S+lUMXuoD1gWCcXDrYzd6hxzvXCwTjblqvYwVx8DFXXnvGJIB85za9k49F8FK7O4wiy2ZUw6Ym0YMVCAYC5rdBHTiG0lj1KP1LWaXiITQrQ5RAx4etAY0eth25OtNy7y5OJ8e3gBm9sJreiNFedAAi5hjM1JBahb21dxLtzIy1TIyDAb3dUKy4R1t01VMagRU3MYYyrjGzs7jfksCKjxgEmSUwkAkUgSUJLElgCSxJYEkCSxJY8QXrwoUL9tYecy7e87RVTcohba+78HDGpXlyTNreniPN4zZ7j4qVa/KpAGkWMMnnMXMg+LiXtD36ZZ2dhyG2rpiTk0O+ranyvIV8S3ODpO0NPlYWeL/FHgfxzgVpaxee2fFuj6V5JMBDJEvzjMW9hnohKEvzuMneseEwDuAUS1OIvXtNmsK5RCC4WkbaFjWoBpWxNqd6pKmqpak8txAIPg0j3x6ic5vkc8subWu5NAuNY4v7NBf5NJ2lybc0a/qk7TE/XUDaupKuIW3TYRao3U3JYkkaCiWBJbCk9wcWbo3AkkLBAoySgoWXV1aeukvvQbwkAhIlAoupioFlsxVJQrwVbGCBR+xgMcVldpqamhr2ZSapogkeeM8RJEjEDhaLLvDEEgj/0YBXfkFVY2KFFV0PAGAADCABGODxzmB5jRZLl7wKzH924wVi3hn/UaqQousBAAyAASQimKuowHJGi+JYYj4rVWABABgUa66KAcvLFmaQZwVwmvdGT6QKI9fpAAAGwBCZqqjAcmyhl0EVShVS1vtGglEVO1g+vCSpWKTeASwvXpIUDS3vAJYkRS+BJQmscqeMSzert0v+oFHiX74uA3/Us1rb5H0nLwus0k7VB40Glwmk3sLr60RqLrBKr7BVZY4q+6vZbpzAKr0qKyNg2D+BVXpVdqkSWAJLYAksgSUJLIElsASWwBJYksASWAJLYAms6P7+2Wz4374ZIrCk+ID1YaPBk5ftePD49Wd5eIsz7diF2h3HCyyppGBNXLyNEzfuOdVj7PJxi7Y++6Ug68a9v/4ZpktglSuwTpzLfpFf6B4yTl2x68mzF037zSL9UZOkfimrZ6xMGzpr439ajCTnmz4zkmZuqNJ6tB3cddSSQVPXWbpul5QJP25LXrilYc9pAktgJabuzeDEMfM3f9RkqDf/X/8bfjH7Tn7BS8h7/qLg7oO8T78d02bI6yCjI+akcgAO2dPn+XtPXCTdbfTSgsJXjKe5D/Nevvq194SVAquig1WjXfLF7NcR5x7lPZ+7dq9zsL4dPP9QxpUOwxaR7jRiMQdgt3DIQGd/+mU7gMz+k9f8vXHSwyfPMy7dAE3G0OOZ16AwhvcsBFZ5mxWCS9fRSw+euhL8f1O/MZxZ/j+aDuubsnrK8p1rdryOHDZ9ZRqZizYexDgxhVy44QD2DMPWfMAc9m4/nMkoyV/a0ddRj6u3TRZYWm74/a9B9ymZV2+BV53OE2t1GI/huZX7eOmmw16wmvSdSRpPP+vm/W0Hzzp7djP3UeaVW+6vXrdJAqvigvXv5iPAaN/JSy4nZcl2ysGXYg2CRMOeUw04BxZ/128/OH4u2/Bis953k0nPW7/PFRLbpFJglSuLteXAGdgCC9wpBr7b9x7jklduOWrsgtfRr2eu3t02aSGjJOnZa/bYKTNX7Wbz2S/55u/jTh05k8WwOHx2aqvEecs2Hzl6Nks+VkUH6+OmwxanHgImK+F81u2WA+daPjO+wOvvfv/GUMi/W4MDH3/1gyZqw+50V0jVNmN3HD7H+ir5DKC9xq2QxZKPlWhrB0wP8dZ9+ZVajPw4JLOoP6aH+OwfxvrfhARWuXXe9RBaEliSwBJYAktgSQJLElgCS2AJLElgSe9FZfejINRcYJVeVWtbVj9jVKv9eIFVesWn8T4om+bqjD68VsrFp/H4iFnZslXRUCWwpD9KAksSWJLAkgSWwJIEllSGwLp+/TqBwtQWUrwETkCVkJubS7BDNYcULxGKHKgSiHGYk5MDW7JbUsltFSCBE4kEtomcCWKYr2uSVAKBECCZhfo//w/mIKeOaZ4AAAAASUVORK5CYII=", - "description": "Allows to define widget gateway configuration for a single selected device.", + "description": "Allows to define gateway configuration for a single selected device.", "descriptor": { "type": "static", "sizeX": 8, diff --git a/application/src/main/data/json/system/widget_bundles/input_widgets.json b/application/src/main/data/json/system/widget_bundles/input_widgets.json index 8a80aa80f1..689452dbf7 100644 --- a/application/src/main/data/json/system/widget_bundles/input_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/input_widgets.json @@ -28,7 +28,7 @@ "alias": "update_multiple_attributes", "name": "Update Multiple Attributes", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXDUlEQVR42u2dB3tURReA+Ss27EpAEUSpH2ABAZUmRHq1AAJKUSCU0KuAoSi9hlAEpPdQQ+8QIpBQQ0uoIQnme7NHx8u9u5ttuLvknCdPnlvmzp07886ZM7NzZkoUFBTk5eVlZmamp6efU1EJQkAIkHJzc4GqBFRlZGRkZ2fn5+cXqKgEISAESOAEVCVAjBPNFJVQCTgBVQnUl+oqldDqLaAqQdOoeaESWgEqBUtFwVJRsFQULAVLRcFSUbBUFCwVFQVLRcFSUbAClL+8imaxghUgT4+8ihKmYPlNFdzkuyTPJbkWkSty1+Cl2a1gFUGVICUwPXz4MCcn54FDuMgtgczgpTmuYBVBlSAFQPfu3YMh6DGaiQNOucgtAgheypaC5RNVws39+/e54ik8twgg5ClbClbRVImiQhX58iDBRHUpWwqWe2vd6Kq7d+8Ciu+v2bdv3y+//DJ58uQpU6ZMnTr1V5f89rjMnDlz2bJlhPQrZpWoB0ssJ1E/gOXvm1auXJmQkABbnsAyMmvWrLNnzwb5YdSBO3fuhCqbpk+ffvr06RDmOzkpB4sWLTpw4ECxBss0grdv30Zp+fsmJtgD1qRJk4zS+s2rBMzWpUuX2rRpU7JkyWeeeebdd9+dN29e8Nn01ltvoU1DlelHjhx58cUXr127xnHt2rXR5cUULGNdoa7QVdevXw/sZX/88Yc0iEUqLdFbgbWJtWrVatu27eXLl0nt6tWrX3rpJd4bUWCRsO3bt4uhqWD9PYKQlZWFA0bANXXChAk+goXs37/f31fcuHEDRbVnzx5zpUePHp07d5bjW7duwSsFaVqfCxcuLFmyhOsotokTJ1rbOzQKZh9J5XudYFG7eITc4P+YMWMOHTrExa1bt3LMFWuV4F0///wzX52ammoSOXfuXPHqVLAKwSK/bt68KTo8ALly5QpZTGvoI1gBKAnUatmyZbt37+5srPnM8uXLt2rVqm/fvm+88cacOXO4uHbt2ldffbVu3bo//vhjbGws6k2qDQqPZrROnTo9e/asUaMGLZctMeACwZ999hmxNWvW7Pnnn+/YsePnn3/OaYUKFZo2bSrBgLV06dKDBw8mSUQixB8+fJhnsSgUrEKwxMACrAAMLBEeFLAws3wBC+0SwFtSUlJgokyZMpTxiRMnzPV27dp99913ckwTGRMTwxcBFkwcPXpUPrNSpUrY6Rz369fvk08+EXdLIHvllVfcgmWUUG2XyJDe3r17uYUW5BieduzYIWG+/vrrbt26KVjuwUKNBwwWj/8HYAnBv//+e8uWLV944QXUCUXIJ7z88su9evWa6xISQNHy4aKxzIONGjUaNWoUB1BlLW9nUyhgmd4x6oo2V44BkVtnzpyR02PHjs2ePZvY0GfoSwXLI1h4Twf2MnLcL7CCt5fPnz///vvvx8XFgdqzzz7buHHjryzy559/egKrcuXK0lb6CBax0Wg6waIRJAHECVtNmjRRsLw1hVTBwF62a9cuv8AKwHhfsWJFzZo1rb8yYTyJxYOBtXDhQlt4T2ABQXx8fJBgMZD23HPPiV2P9O/fX8HyZrzT9wnsZfSMfDfeqeIBDDegTd98800sJFnmBBsLnsaOHcvx6NGjK1asmJaWJkULCphQnsBKTEzECCMY37548WK0XQBgMZJMW8xnEgmG1zvvvEO7rGC5H27AJqXei2Xql9CXxHodP368j8MNAc9rxRJv2LAhqoIuHkY3ekKGuRk9ok2kawYx9NSSkpK8aCyYg06wIJIOHTrQ0QusKZw/fz7xc1q9evVOnTpVqVJFwXI/QMrIzYIFC7Zs2eLvmxjBooR8GcdCVwU/DR9tQQE7Z17wFYx6+Li6DlMzAvjxyiZgHfCQ8lMOVoHlJx3sBuo6HSvpovs+5k5vn2pN7fT0kw59QLQCdpX+CF28wDKt4caNG2GLTpOPbBGMwDzCg9YpgZr7CtZj02bQKCBCg0ibxYHYCm6FWwQgGIE5MFOydAq8guXG0pIGkZ/YsOLnuGTz5s30tjC/xIeCA065KHcJRmAe0Yl+ClbRVvy2bdukTeQ3V0wuAJptEU65yC1pAQnMI0qVguWT3sJgQi2hjRL/kYUuMafcIoBOeFew/GNLJpQmJyejkLY6hIvcIoC6fylYfrClDqsqoQerQF3sVZ4QWE7CdFEQlVCCpaKiYKkoWCoKloqKgqWiYKkoWCoqCpaKgqWiYKmoKFgqCpaKghUS4afrJJfgtIPzdMBLRTgFZ0B8gVgRRJYQsgpeqUmPC/5CvJrpil5W7/Xr1bijKVjhFAqSldMofuY044TYu3dvLwsncWvAgAG+RMtKDSz2QgETJxjZ7srkRKa/du3aVY5ZK4tZisuXLw8SLCKhquBOwtIEZoVIBStsYOHLL6esGzNu3Dg5pmBYCmHTpk0smFbgWkht/fr133zzDQ7ssoQzjkAsG7Rz506noynTWfHDloMZM2a4fTUu+az1YE5RbCy2K9HivozfKcCJ3z3LaMG9dRkLwrBOH6/GV9b23p9++gmHbDz9WZHGfJeCFWaw8CHD2VVcx4YMGQITKDN0D+6KV69epanCD5YrsjgqRYgHB47trFhkK2CwYH0sQCQMztC+gMUCELy6wLVkHG+BbyhHg+KGzyIUHP/www/iFE5IHpznEgLgQm2NlnR26dJlxIgRqrEiCKwC18oIFC3cYHLJFdZgZh1HKXKzEqS4aMsxrtUwZIsWIACFtY08vdoLWOhFsczQYTSX4qSPAmPRgALX4sfQLE/h5I0ys0YLhVhscCn6T8GKCLAoTkpXliE5ePAgDQqLTqEqKD8bWKwnwyp+o12C2jCQiWBaQRsabuTIkWBBJM4lFbyAhcoxYXiLHLP0I+/igDhZRERejbI0kImIguQTQrhCuIIVLFjHjx///vvvMWuwq+BJDPl169Y5wUJ5rFq16to/YnXRBpH27dvTyyMegrGUElrHaZUHDBbW25o1a8yriw9AUQkWENCVo6TFjkFdUaLiFESzggaSXiENpSCCD+PQoUNleAITmwXfTZw8xSpCsjgWixLyCNrLr6bQO1i0fcOGDZMOBN2LYjWyEGVgwQGqiKEE+lmmS0g7iKZBgbFqzaBBgyQwYdBktGsc0/yxnizmOWa+rf8FaixhRe+MAJhERMLy4KECi1fj/03MPM6rA1g/TMEKs9DXszVhnFoHF+DPS0vErSfnO8Srg19PS8FSUVGwVBQsFQVLRUXBUlGwVBQsFRUFSyWKwOrukuDDqChYCpbKEwOLn8wGDhzY3U/hEevmlCoKll0CoEpEfjxWUbC8tW6Biea7ghU2sJh5whwY5onLZvHWW8wgYAV5ptAQwNN2r0uXLrVNQWYWDc4OtmDMoCIk8z+ZniUOGk5hc02zDy+pYsoNr2aOqG1aDlMbmAXP7GdmINpeLRsjMsGQt5w6dcr5CiZc7N6928dNwpjfgTcROYMrm81/iblouBUxWY05PBkZGZ5iYD4IMyWZMM30bma82e4yi5p5i3yj2c366QELHxg2o2fnSPZ1fvvtt9nM8uLFi8a2K++S1q1bsyvua6+9RpHYHocDthoU5ijUDRs2sKNdyZIl2TfVVkJsF8iO9s2bN2ebcTY3lF3mrQJt7EYuW9uT42y1ykt5NQkoVaqUmdEFoOxoT2KIqmrVquyKaLYLZaJO3bp1X3/99RYtWtSqVYvYpk2bZuKn+ClC3s4OhrimFZkzzFIkMPstEht7qpMGQypJZcdhUkjySAM7LTKp1RkDXif169cnPWz3ytbXZBS11NyFfnb95FvY85GkMt86PGA9oaeYVffee++JokIxUIrmqTp16rBrt2w9RwUlaz7++GPb48xnb9OmjRzjccU2499++22NGjVsYDEHFdpEGzGzvkGDBhS8LaoxY8aYpygttkgVlwqgZG9LNs6UW2gIikG8wUD5yy+/rFatmtxiaiEcmOmsTManLGUDWBQbG3aSML7OR7AwbYFJZqiSBp6FMLkFKCRJJmSTBpJKzXTGgD6DKqPPmLRI1RLfEOYzkjaZuigfBWSBdbYiFCy0FDN9zSn2PnucSiPIp+L8aa1hqAfr3D2aJLQdTY+colQkrykAG1hkvSkVhIpL6Vonf9JkoJ9kb1XZN5WNF81dZoqWKVNGjplQTxU3t3BHI7DA9NFHHzFb1dzC0YhbslE0eInOQ1P6CNaHH35ozUDqBppJVj5HX4rLkAi5RJxSOZmTzZxboQdc2DLSBMOXiWDSdtOUc2xaZOotWlx8C54GsCCDeoxpZa7gS4g+cLstKgqAkrNeASm4dHrwOcH64IMP+vTpY05TUlLIVmsFxdeUHaY9bcyJFjQwffrpp9bvwueRqNw6e+Gjhpq0zV32HSyUH7aROQV0KwpWwdpDY0mVgyoqpFuri4YYBSY+mGBq3cgYodlFRz4lYGEbkVnUHmthcMXpQ0z5UV9ld2cjNII0hc5onWCh2HC+sI7M8RargQ86TJ93m0gohHWjFykAqzmCAiAqDGTbU6goQuIfZrvuI1hoUPiwqhAsOR5kNQBbSBJAlUhISDB11dY14V2oLlICSWa7a3LD1npiG+BkEAVg0TrY/EidQkvvC1goefbuxrCwtoP4HGMlpKam+gIWdq4TLLSUiR9qnQ4XBS7XIMw+3D3MlYoVKzrBMtgZLGgxCWnr5PoOFjqbYE6wbD1NVCymJ0rUi+P19OnTgQb4sNLMe6mQTrBwH48CsHwRACKzrF0VvEy5YnWZJ++wtWnLbH4T1FF6NG6jdYIFHPHx8eYU05u3sAyEnNIVtzWyZrCDvmS9evWsTSSmutW9hz48UZmOobHiKUi3ww0+gkUVotqI35sIHm/GQjLw4eNE14c6VmRW05nF/sN4l0rLEIaxGo1J59SvkQIWDRZtfG+XUPDWhTQ8ZR+qwur3x1PYAda8k6pvW4KBBylgmxeyF7CAw/oJUvtNIUGP0/eQLmFsbCy13KY+v/jiC3wVzSl0EtXJkyfNFXKAzqlzOMNfGwtirFpWdLl1qQhc3NDEztEpT4Jdb5Sr9G2tTlAoMPrFkQgWiyw4x7HwV/b+FKqItRLMKaNQFLPVYMc8dyYbuweLwZMDlhMslLwZFJB+OJ0gcTqlMtDZtNFDjtP8UbROf1TUFf1WUyRTp05FtZiUiMHOcJqn7/UdLLA2YxwInQ/0rnVwhBrotvk2wuPYD+aUwLyarOMY+5JjhuvMwBi9KGvTESlgUTwGprUuMac4znt5EDOCkmAVK8YD+U8hmUFFejHUKvrSRywiJe09SU6wGFkmHykMbCaO6XCZcQFGd6wayIwhkSpMXeurpcWBCcxqBiDoeUAJFd1UDFor0o9XrfUp8cn2AhZuuk2bNnVWEkxPXkRukDPkJxVp+PDhcovtkrnFUIL1RdITxFmXhlhG4Bivl0431YYxvMaNG1O7xH0cm4y+BYocIxWqGJ2OiYnxspl82MBC/3sCy3RYPFmp1EVKkezmf1xcnBlrQFc94xCsTsqAXPbU1rgFq8C1KA3DP8RAXtOdFIuNXCZDAcIWmAba+WrT+ef3JYwVrlC6jL8bbUer7XzKZgg6wZJBNdtKTGYcgVaVu1QwlK6oWBkwc75ITG/RoFL9ZPUKFJsklUbcSjk9mJo1a8qzGBvOXzUiAizaLCtYtAXm1GrqevlRDIvHttaUJ0GHMTIegJcz1ZRfbGQo3GgFfi1xO2xWZFRoiJD411NV+H3Gi9FNzvjldW1jlIyCM7fgSsecu8G4jP93YPG7JvoAReU7WMVWYLpcuXL8Oh69n/BkwTIYidAywhY9fI5ZSEgB8iI+znR4CsHyPtFPRkEZWbBdF6qKNN5Vol2CmprMb8OewKIPIuOHjCw47/IbrWa9ghWsoJxoE2WAlBZQdZWCpaKiYKkoWCoKlhsZr1IsRTWWijaFKgqWioqCpaJgqShYKioKloqCpaJgqagoWCoKloqCpaKiYKkoWMVPDqderN5+ZEzDfqUaRMEf6fxfu5HJB84oWBEth05fiGkYFxVIPYZXg36kXMGKUMEXtHq7EVFHlfzVaDeiSF9WBSs8gs90TIO+UQpWqfp9vay8pWCFU1ifI1qpcv3J+iIKloKlYClYCpaKgqVgKVgKloKlYKkoWAqWgqVghU+SD6T+tjQ5Ny8/ksF698v4txr3V7D+O7mfk1uxxZDe4xebK5tTTnJl+ZaDPsbQL2EZGXfvwcMIBKt0w7jx8zfcyCpcejQ//9Hmvac+/Gq0gvVfCEDw2V1H/Ls92Lqdx7iStH7fUwDW2DmFm/Os2HqIDxw1a+3d+w/PXrhWJhyqS8F6DKxlmw7sOXo2Nf3qtGXJC9bssdKzdd/pyUlbth9ItYF16FTGlKSt81btPn/pRtjB2n/i/IOcXDPNZuLCTdl3H8T2msJx2SYDeo1LmpS4eeCUFe+3GMKVxj0mDZi8vErrYRK409C5fSculePa344bM3vdyJlr6nf7RcEKAVjvNR/8QYdRFZrF85+LLfv8vaMJWcxp5VbDyscOqtBssAFrwvyNHNfsMKpSy6Flmwxcv+t4eMFaue0wzw6fvprEWK/zRafPX8l5mAd5pPzqjdtV2wxv238GgQf/upIAGGR37uVs23+a487D5j3Mzac9zbx5Oy//0Q9jEhWsUIDVcbTYKJKh17PuZly5WbpR3NeD5zx69Bf6oNH3CQIWig3d0GPsIqaIYLqhACq1GspBGMGq0X7k6fOFG2Hcun3vtyXbjIHVJm76rsNpHQfN4pgPIQB6C4MMdLYfPCMBuIjp+fYXA25m3zucegE0aUP3HT8HhQHMNFSw7GCR+3KdBo7rVPQ1O45ysH73cZuNNXP5Dg4OnEyX64lrUzjdd+J8eHuF4NJp2Lydh9LAnZogupY/dG3PcUkTFmxcvKHwSxMSN3Nx1oqdKCe6kHwL+gzF1vynwi3p+FhaSf7o2XBavd1IBasIQeXw2V2G/7sBmHCzZMN+T2DBHAfYXjawJrnKButYrq/efqSQvxC1hsGPY3363YTjf14Cr1rfjEUNo3guZWZhC1rBatKzcAM6qtnZi9elgok+u5h563jaJfNXt/PPClbRUq3tiM+7TjSnmORkxN5j5zyBteNQYWORuO7vbQrFwgWsVclHrJqMfj6nJ89eDhdYJB6MGGYzV8bNLVxMH1uKtHFQv9tEAc6AxV/65RuiZcGL07pdChfmo+9iIgmsU1kcwYqfWmiuDpy8nDKgBtNFwhahIfAEFo0FtgsWOgwtWre3XOwgAQtziut0oIgHq5lnm/aaEl7jHe0LW2DBV9DwXb6WhUlOx2LEjNVShdoNmEkryfHUxVvlkcmLtnB6936O2PuYU+hmcoNcatVv2vzVe1KOnVUbyyd5mJtHXxrDQqpjx/jZ1Fq55RYsjg+eSq/p6ieCIN1y0yvEyBU1wB+K4cr17PCCBfRzVu4CJokE9UnHVq7T4yso3FHxL5pC/q91NXz81XOpKMaHTSRo9A27TzC+ynUa0O6jFqrG8k+u3bzj1y8z0lt0yu27D/iLnJ90GDtAlZZ3aVbrHz8wlHNc9PRH9xCbvXSgbkL6W2GEiv4IraJgKVgKloKlYClYKgqWgqVgKVgKloKlomApWFECVhQvCtKgr4IVuWBVbhEfpWBVaz1EwYpcsJJWJ0en0uq7dsseBStywSLb/9i4s0rLeJabipZlsaq2HLxq405SrmBFqLBwWUZGRlpa2ploE9JMynXhtQgVplXl5ORQQueiTUgzKdelIiOarTyX5EaPSIKLpErBUnlSomCpKFgq0QVWenp6fn6+5oVKqAScgKpEZmZmdna2ZodKqCQrKwuoSmDt04eELdVbKsHrKkACJw5KFLjG60AM9XVORSUIASFAEg31fyyc9OkBPXzZAAAAAElFTkSuQmCC", - "description": "Allows to create an input form and set values for multiple attributes simultaniously.", + "description": "Allows to create an input form and set values for multiple attributes simultaneously.", "descriptor": { "type": "latest", "sizeX": 7.5, diff --git a/application/src/main/data/json/system/widget_bundles/maps.json b/application/src/main/data/json/system/widget_bundles/maps.json index aaf958a646..41d5ff55b2 100644 --- a/application/src/main/data/json/system/widget_bundles/maps.json +++ b/application/src/main/data/json/system/widget_bundles/maps.json @@ -46,7 +46,7 @@ "alias": "route_map", "name": "Route Map", "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAABrUUlEQVR42r29B5Qk6VUuWGcXEMvhHFgW9gg4CxzYsws8Fv+ehATvPWTQ0/J4T8ADNBppJCGQQQhkERppZpjRMNIMmh7X43t8u2rvXbX33lZXtanu8pXeRJpIX73fvTfizz/+MBlZ3do4MTWZ2ZmRkfF/cf397sCtW7darVY2V0im0rNzyblEKpXJZvOFYrFUqVTtWr1WbwTuqXR23rvNzSXwgcA3lyuVortlMpl0Op3L5QoFfEsxn8/jMf6qN+Ap3lAM2fBx/c3GlkqlIj7rHCGbxVcYL5ZKJfzYiE9VqlX8EHw7/uLNYW/L84ZDWaVyqVTWr08+jx9sqWuCaxV4BKtUwoX/vu6lcjlXwHXIpdKZZDqdTGfSmRwwkC9aBatULJVlBxKKlhVxHPxGWq98PsNbNpu1LKvdbgNUA/jBhQKelssVOwxGdaClkm9ac61Ssl6rOsDKZDudjn7hAEkAKARYVQMc6jHgZSwzFgb40J/q/wrcCCIDN/xrMpk0DljwfiCdyehAVxvOExci7Mhyz+AWwFXCwoS9Lccb3lyp1nL5gn59gJhsPq+uSRiOrci1vP09m8snk2mcW77YxVDgnitYuPYRh8KyZnwb4AVgDDSbLRc9+F+1Ua8265V2rdSuWbwXm+V0y5oGpDp2vlMrtsqpul2WS4wrrV84vGJZpUBgVW07DFiyGMZTAEuBIU93ehcb0QIJR/YDi0VwQXuaM85HNhYzoaKoSuK7gcNAdAXiUp08TlXEW9or1PGl6XRGvyxhOI6PEiwtTgZSAdIRJx8HlIlUGpIpGlKyQ3pBj0WIK1xVLIcfW5DoA/ILG7VKy5rtFG62S7NAT8TeLiebVpIuMX6EV9TjcMBAILD0uxOXHqCOBhbAoQSVriuxZsZn/euKzxoizfgIFCFOKQhYtVKpHHbYEgtjyCrsuKxhJ4Cvxqlijf3Aajabc4mkB1ghOAZWQpFRqQK1Jd4g3YIEXg9NCiTkrVIcYGGfmZ2DhFOfhSrHbVkqV1gFWbjMWPFAoTXQsabblVTHzhFurJlOcSIaWJ1KulmcJe1WrmK59QsHVYpjhtlk6u702zeGssu5m67gFEQi9KCsq1+kZbVDkeZlfeRfVCxYxauyRSnPwnhMpsROgNwis8P7tgChxYoVa9BqtfVLNDuX0O2NMJUq2PXt1aJ7DS3ewk7AtNJIqlXVU5wVhGpMYM0lUwnc5CSnaxDZfmMJp5IJ2gY8oAG8cmM9gGXnW4VpEUKQk/pVgxOQTKbCgBVhl5g4gObCTa9dOAihYuxNJFzRa1ThJpKdDKxyhW8MU52RmVmrKZU6l0hgT2eyuLLqguIB5H+Y3S0CFcjGe1hvWoa1gCtWKlcDfRqvJ1EOUj2l+BdBl1s4GR1qkECZbD4msKA3cSpha8qCPw6wsBenegCrVqznp+WgcCH9jmHYGUTf5QawDKnTF7DIN/R+nCz6VAo77mUFEf/5iIFIgLasPN3SFfHddItbzFUID1F5QLB+NUXLs88rQrGC28ljhkJzeBxDuycsuq5cP8DSDTWcZ6PRUE8hr8jdiwcsfmfpTgDL6g2sSj4plixsiEaz6XUMUxyjCLCxxAaPCSzjnbqTqGRDhMRKctRB5FOKzcsUBzi8/kQt2H73+R9Y51q97oCMcaNvWQ5e5N1NpJ1EJfB+rGKkY1iL7xiWypX+gMXWGIkrq4QQANmFFqz7MpANORQTWBBvuRC72bmM0K2xgFWa6wksu5STqw9fC6duOoalciCwIHUMZzDm5o9HiHZLcsQt5/qMAjWxsUQ+5ej0nBgKJE2hUAjzJ/SYApbVsCTwY3mtS8CQBOHEFVKhuMi4V87jGFarHscwPHIW7IiF21UhKrVE3kapDCsFf22OmOA4sBljAitPFmo+AlgS24sBrBh7rUS3Jt/KdMU9d6RVwqUOsbEqiUSieIc2LG2KpEVOMKR2LLjYEymvY8+2UTHMn1AIZq+Q9JAnCNenqBCUl/gGg3GnXx+oJMMxLPYTcegLWIJsRlK1VqOgmgpfJ2NLLOy4EyLi5HcSWG27CFOCxYBt3JG2bWe9GscTrE+lIoLmsJSztCHsDnD0WEuIqRxLIPxmXLgsqz+gCpfO+bVZ0z/N5c07zwgucByuDFzpZlBf1qE6Dva5OThTFkKR+vVB5BAOvL5OLA6DIg7lij9c3tdpiI8i518jnWspYGHVYoayyMxKZSqR9rsI8jsALOy5bIYX1bTfEW7F+kacQRiwgIPpmdkp/DcHqdYju6JsKf3gZO9r5pEfWFkfsPxOGRYPStN4ZzU8aqU5g3Q/wClOJFIzM3Nzc6nZ2QSwhSUx/Bu8R3cMwyJnKqBgSVqkUu3XeFfAogcAltUFFrQK8mJxtWHBinYM8RPuDLAa1QJkj9x2CPDAMPQ4hl5RrxvLMLOMWCXAgVdwrUnrF4s4mgRL4gCLTKJaTUNSTl1KkU9G4hISzhDpfvsdi4j3ZL2gNExsglGOsmyAztTU7PjE9I2bkzfHJ6emEfSh9cJp1OsNI9+lNooiaRIxQhwqXdYzPBEWDFOBHgNYeJyCvRsbWHmvCPcHkigJWir1CSy70CknW4XJRnasnhmr5GZhvgJY4hhCqMJjMu5IY0U5Tm3x3ZwQbAmeACaIKJFSKuWCjwqwjFXHRzJeUNICU7gLNnUO/wRVqC6lJAaM+wzn4PdY3SxQCnuejX2RdjoE8RhnB4E0M5uYmJy+OT6FfXqGYATDBRZdvV4Pg5F/w6LqEjFMHJaCnPyw+FnYJr+Co2JVHFGtC15JJOOaWdCauGmjgcV2KVlBQcBiALULk63stVZ6tJm+WstNlQupSmailBovpCYzydmUtskq5kigVryOYQ7fou6wJG94f9LZCF6wu7GKWBVACrDwRSnrsuu2Ks51emZGx1aK7XdypjmwmWPH2qtiLENil8pmjpwNcxyhW8eBK0ShCoohUr4CQQv6olQG96REUKHu529jQ7ZGdwztEKwEAissPBEKLEaShN1x/3dlWKVCJkdMYBUQ7slGAEuCGhIw6wILSGrkbjYzV1upS7XUtUp6vJCcyCWnM6mEAEhSPxKqEX+HTeysyHPIhbyRw7cs5RgWtAQOPghUYRVFjfplkq4CyCQ3situ0EFEl/iAnlvfG8k0nrqywYq8QHaSwYqfWiTDpswwas7f0a1e9ziGETUOdwpYNpVa2HUWz+pL09kcRHq8iENpLpmOjjgU2I0l000Bq5iZKeSziCJnKfrjIMmFUVQRQYFNFlqMVMYbqrGzLDn9lwz2dYlEa16A1a+3JbFTwhMXjFGKxuuBGga74Z9S0JljfZCjOU6sQlSN3RxPsvyYTSQAJcR77iCGcDRoSeOYQY6hFYissEh3v8Cin1ytotJAjwSRRI6XiiZgJVJxgAXl0QWWRBZxd1YJcTFj40Wp1BPrEvefkcNXGUPjkklqWdI1PUMy/jfgFSkcUALPCKZTEMuV9hXWaFSwaJWAJ6e4jy8lopRlelt9x+49R46fOHzsOC7Kmg2brl4fu00YwUaGpsN5ys+kpGE+j78GthKGYxgSRwiMHvUVVFO6r8yZJSWx2JUpZvOFmMUzs7NJUXkwGgqsfESPYRNDVgIiekJ6wGuFlFgqOC4P3GZcAo7uWCqurVIWdFA2WRBta3ovnMoYGlcBHxf7PdbdVq8bqTRcpjyLSbUr4QeQAXAqRiqhcVGdpNFsHAzVijX8kCWvvQH3ELcv9u1DQ1t27JyepWKNdRs3Xbo8EhNDcIQR6sRhcXAcU/I8Kd8msf4C1XJ5gg5QQ1jSno5hYNXkwoCFlTWARZHPbMyMYWmagZUJ2cQrBKpyuo1lhKdhd2MvUyakpu6ndMgm2Ru4DLjEXscwLe6V34qKnzEkYGkmBeAOp6xEKKmRtURpLCfsLmlBiZDhCmIVASMsfJ3NVfpbKp08dfrEyVOA2gtLXp2YnBRg4TdeuDS87+Chy6NXro7dGNq7LwJMOKyITFw1KYBWAJJ7V15Ud50UwYnTgA96HMOiZZiDYUL7zgJLt9vKZFDGjThMzyS4tDNHZx4UDg2IY0UoTkg5SddLgkxJP5HzlPjjE2VLt+QrJS1HF0n2Cn4WgCoIIbj3iBKN3ZjADgcfa4CzsrjMjWx8xn9D2yBCoN0uXBzG430HDkIOjU9MAA14/NKrr589f+Hk6TMziYQAa3J6enj0yt4DB8+ev4hThYBG0AA2ELS5SCPOf6enp2exS9pAACR193Jx8FRMUq5zVFtBVkISA3iuXx+AwyglLcY2s+x+Ig7KtcQq4Hfp+XWcGOIlscv9CFioXTf0XSxgkUJBqStuCOe+zEakWnFBcSOKMWhxDtxwDPNuxjcsshyUac5T8VMiNTk1AxhxuGgSj/FKljIQuCh1wGJqelowhG8ZHhmZmpqGWXNzfAKv7Nq9B+ps1br1UzMzEFTnL14c2rN39MqV8fGJvfsPrFi9Zu2GTecvXlq/cTMgCWABDmfPXTh/abjMyWqOx1j5XptRzqCKGoBx/RW5MmV2cXEZ9esDXWCWkgYldgpFK7oWt59QFhU42DVPfDERG1iiCnHNwurcA4AFcOAqyS1o8ZWgQkoUv5LiLPhs9gziH1Sums3jfXL5YL0ZFX/QGhw4DQgWKwDBwMdxpqZnJyYcmQQkzcwgxAUtUaDgdSMgeH155Mr4xKQAC6Lo1NlzK9esHbtxY8PmLXjlreUrseTQdOs3bQGAUPkJTbdq7TqYdRcuXrx4afjS8GX8NHwNBFKeC6oKLBhUd83tbBRH1Y4jeXexFgz7HWvMjmFNj9P6y1DDGgj6ApYERXF7kxj2AisVOxU9M8eJFg4CVELqZExgSbIzuphYpfNgRFQqjv1V01IfSIqZxd1uDS50Yopj1pA9gI4oNezIhKDbDAhDWBM3daMRK1yEEzhy7DgwhDO4cv36mbPnIZ/gze0a2r1p67bXly7DigJY23bsvDk+jtPDX6AKPxGnzD5svcYmVzRE/AIpFrDsmlT/ycaNOrYIdZyJYYZCYnlKSb2WE8nFPPWqBK8LN03EzRi6pfoQ4ZLV0KoScnELlBNpBMLkVokrsaKDE1yakhXNiNQLaX2GFC4iGwppN2NoetSU/qMexTQABLEEDAFeUKqlcn8JEDNsXa2+9tZSQAdqAqgcXLN+x9AeCDmc2/kLF6enZyLw4QQsCkV9+YM0HXqE0j21of9FCBj9KVeCNSSkR1/utd/pjirqFk9NwtB5ic1qtzqHN6kYXyVk+gqTitgrc7oQpyRnJTu+qhAvlJVIZiSsIIGGhQOLzAV2atjRSQtKcOvLK5zxyEh7hptcg5VaM4QWfhJEy50NW0OJ7N6/f/fefVCIgEh3921iU4vLpl6kYjeOTejY0hwUuHtp/FIxDPQ3+F8Jw5yqOpRbLuvUF1VxifQfAklmFNCl5WLWxI6pcIFeKSyyFT8bLZ4gd41SzEUPYcBsjVk8k0o7abqFA0tcX1UnqaolxWIVQ178QfxInK6EsqgEw9sKdkc28fKkgtTp22GR44VUQU7JWGwJ4SqXTSKrFK2QLGSpxP9K90yK+oDhqSAbSIqDW6+q+qHw6w1ghcFLL9NTRRbkGNo1WMreVrmyUYpoND/2jGzF14aSIClzXkEvAIH9NxuvlDTr9nDjAsYFVtcZdHvbVSm3fv/pGzXZ8a1PoViWtJwDL9wpJKlSJAGHoCTFhcjUYZLJCgeAtES7a28CS6Ag8RFHlrhXWXa8AOFENXBuJpXKPvnb4XxAZXAoIS3gM9ScHB/BXr+2Nfx8iIwyowzWuqcislYzHMOst+MtrK9rAeWH+DE4A5wSRRz0I6BGOV4qGhoTQkt80rjA4qwf5fflJpNuAjfQkA/ElkgOuQ8krCClwPHVGWSynvpQkUbVmyBCSF8qBfeqEga8qWU27B4Bpf4eCW8qYOEw+EZu4LNFs6ivY8FWg+7HR+UxVU9wK6xcGTzFRwAsFRH1B36dWnsK35Qk8aXnJyA8DMcwHy+hhu+1+m3X4aXFAyNGKlmTmGbW7FxKwbFMAQFTJ0qxON2HqTTE80Cg66F0OXe7FuSqKfkva8YUBhXJ1tkcmIkAE0wuiVzLja5WSMlFFYzAa5KHMWSAP+cvKU9lUUncUjY9Mi6XVXaA1uKyJFYNFb2LX/86LverSfhV7XhV5aNqfGNQ/Q+XA6nT8KsttufIMYSrgcMZzase1VYqFeIna/svJZXov1QydiMOEB/E/9IbWElXcVORNJWzltMujLh4gV0z6l+UmGA9OvJekVtf70vR21TksdtjmDR8PYAJ4pcLRJ2SLANGQcULOTmtiCJSdV2wZpLllfC3wpbl/jaSo6m0KkklMc7VquLBSRxO74vXT4x73G0JTLDMI1cGSFIxd3mq3xLGEdQ9ID2GSA8aZijWQq+lXkCtR3+OYaWCBZLfpZfdZnL5GDZWcXIm0e2sFJ1CZWxVO4RfaMAImnWThiwPRDEZPU9yBaW6gXQTHzqZzhqlS/zxHNtw1bCCBV81TlbPMQe+X78uykLnVGhFku1GlrqkVQ6yGZ5VkUNdxvgRj8OysnN6VPB+kVhSMiT615B5xhH06p0il6kZrEY5rSMt7PfeEWhJc2y9zolzTUzC8PTF30uAO26DDAWqqRILQazZRKpglaMrZ0xg6QmpKqc15JIZ3ZiBVjwQhb8iaQF8Q9STje/6IPhtxEUR3qLjNqdndFgEtg/oHSOiBHGeIpZ0v08Bi34F3xsCLGUg462GFvafntyZkvnGX50DJ7Dly/hXqYbNc99Vmcp4MkbzqlFAlgu/Pre51fhqEPtLo6E7hlW2YSjGCKmfkeaDBD1jiQQwVSMbvxrNVsNlKzKBpZRajvJxlihJEUJ8k0VtRM9CblSJsW8ZNcpcM1xVni0A4Nr+TuiIo0fdAJL87Smx9EQHNQXAAK84MkkqN8iEYnlv5GvxNvnSQOVlkCX1u6lovr+Uw3I9ozlvxAHCI+ntaPp+GFgKWGLGwWmwfEXPdhDbR/QOPInh03F9kSaDDH/Bg0L8WK6Oywa29egw4pA0tENSUZ/VuGRKHEPq3faK+jLndN1Tr+NLxF73i0AJKYnEEnoFCYKEyIait/EoK1XLYq1TMU8XjlagaAk02ANFTp8VGQHehlNfxDcDlI5uhsJymPVSXfTb4NWrvVFsGETpMiIpClzjENZRHEC412gCQcAKo6VNe8uBjhCc4AZGuVGbf1SbNzyYmJrGiwNSpBYILIkTimk8S9scZ2lSYlvIGkj0xWle9QaXEVPQDyst6qysyobky1DGsCjV/sAGQmIKHK5eRkN92i099Ri8emFdzzhQIJdVTEoIyypFLLzEMvwfF2UtVTS4PkYqIiarUZwfgoWg1D67+omkU2EutSkqWisRh7DaVI9A0lK3ikVBMefIg+Onz0zOzO45cAi/68b4xMbtO3bs2T96bWz1pq1orBxQd5XR8JTnSBL7Po5XpwIEwpqHxBzqmam9kw0FfMQILkv9e9eAAMkJ5+B8UjDNvhu1kXC4iNqI9focx9dzq1idNWZbqsbxOnldLMK+1kO3vgXKEcCiCrBIxy1QYjknxvdermBSXeCq6qZ0HMdQbgaGESegEslZzslm+XYsl6NoYwtOKKvUG1gspVA8gvcvXbX2xJlzWJ/9h4/i6ZlzF19+cznEElB1fngEhd2j165v2rFr3aatew8e2XPw8KadewDxAUUbJAl5IdhM+yr7goQ8iKYkMencAYihqeZViMsbN8f1PCsORtKIzWGqnGGLSgUgqPLTtjnTkjcKvxRfrQBdECmiFEBUYBIP0d8NGw2sPiQWlxb2Jf/Ut0hQhiruvanoLIeaI0pJXfeTLl2C23rnyBShmJHlNK31YRjhR2BdjIhD2A79Nrh+A2IGz73y5uadQzcnpwRYNycmXlm67NWlK+Aq7j10BJb+/sNHLo2M7tp3EMfFG46cOAkpNKBnciTQrPIhcRiYxDCXrgrYqVB/VNly/MSqtetPnD6j+xTZnCOcXKQWFJOMXLgMO7j8IBMmsXSxRFSfWizKIL3l+vpkIpGMtkL8wArDlsWdxD3NLD8yRE2zYwjvIWc4hjq5pgrdpSmMTAzWzKhDt1qJZFF/9nUAsKwSFc+gxiEckU40ldf00LHjg+s37ty778jJ068uG9w+tAcfX7dp85VrY48vfh4mzesrBmG0nTl/EeLt9LkLazdv4zYk+mkD0rIn4loLuPeWxiIPqIuBFZN8cPjy6CtvvIX94JGjk9MzZhFOKh1N/UM5f5JhhUBtJTjThVB04IeIspKpiK9TafU4wHJJjnoAq+C9UKwKChJm81sLVBHpjTiIw6HYl+7sLmlQWN9lFgQS28SJ4XZARRwAjB+IWnB4Wmjpg0OP6/voU89AUEERLVm+6tjpc3iwev36TVu3Xhi+zMZWTW9ra4uBj06TZmtA6TX91pGMrBBNJZgxEbtKjSneOrXpjaAIg6A3Yfng6teXLteb3CmQQ+KqP/vaoPaLoBUpumTMOjIktBGBg76AFZYMwD0JQ3Rmdha7+BCuq5vKeUtJcRmNpLsRcRBL7o5DSt3bVOPQbpNar9igm6AyWvCwJSBQbalf0AENdw9LCcOcfL3pmSJ3ZECZigvS4Y2b3qiKmMlUMC0gA12NwIoDLAheoY+SMGOKGTiloCAsCaP4NmWx1dlcG7uxdtNm1JIbfFTUfcvZJZETQBjvWcXfF+auG7MFAmNF6l/9yDOMNr+5bfw6/48VlEjJnv8gUP1AlZLZKpvO8eGyygMKXRYkll4RiYUxIg4RBb1xdtsNhNpcjMlupsUXxMLTHP80iQQV5MowUW9EuEGMZmg3wBH6Dq9IT3nOYZ4ugcMChZwzJHhSxIlScAq8BphMIYk4ArcpOzWv+dghYLk781pqYnhk9K2Vg6gS3rl7LxL4XhqjnNhSfsHAbnk6gLnPkYhdiWV81uCV7ItUUu6NQPtdJQTdBq+c2CV+5QsppXt2TiyNgESsEIJ1VfcHd6nmrYhMptK68SSk7VGxJV5XIbgTzUsPmApacWFD/PjrWOjMkbrN5SSCUL09+IoVBEixHxGQXhygLhoqXyFTl6rr+4/C6Xmx6D3HtrkfVbh1AlGljHc98RLIHBnh6EWfvEqNC5gESQKpLOc1xEiKaKfBvSqsaFKzRaZCXhqwuzkMT5loxZOfIPYlDZdCUqKcRKlRwUck/mfzhqf4i8ClhFu5r6vbkoVz4O+1hSRSOgHVMXFi0q5zO3JRapZgNM6El3MN6Hk3pvO2i0FU7N3FoKufYQ/OY6Bo3EulN1esfPr5F4+dOCEEuEptY/kklNWNxzBfGaMqHxgQEq8q5yVqjxhOETaHx0jgiKwSf1PAJC2mUouW0/wY+ZAewwSSwHuDHbKKqtT5oKWyZPqDa5AoQMV0SxDhBh2wZAxBYSdIsjQmd+Y9IAowhSEKcHPbI7wz/EuVi3kkDec0mgU7g5bWa19xGjB7W3L1ZjnbKmewd6qZtjXTxkgAa7ZRd2qUARW0y/cGVtEllNJ9eH1hILSnpmbgNeBv2i1z8FfNHjt16sjx41u271i6clDIktXlFsmkVTtT5IFjDSkpJZh1N8T4daFlAD0iGSxwKfpcMzcYRoVWWbdkWVaCBXZOqyztDm4RSmCxNen3FiR5XeFBI9Wq1i+uwwh44KwlvhccYGgXSOauXKkcPFRfvbr1zDPtb3yjdc89rb/5m87x45IJYX6vHE2JstGt1MAryi5ucLu2kE0UmSe8WJKSp1p86UJljG60rMJFZsR161e4xDxRaFipenEOf5uVXAd7NdOpZHW+tFatLCYBzgJ2eqAe9ACL3c58YAkHrimIxsYnplTNSUQ3Ny4oGk3RHYq4rc/dLUU7gCJsJNJv+G4RrmKETSavSPOtWgwRSwruBVagNqeuC+4RxDZy645KIg0CmNAAo0qVlSCCTzmKYc4msiOj5QMHaoODjaeeav/zP7c+9rHWX/1VwP63fzvvyiHASNglRNOJ7mN9WhZzvi8k+XPMijG6SuKTCrOssqcMpmFXaGhSJd2qZjt2JANtTYrf0e5RoSqIkALUAb0CmtVWrRySsYou9deJwhATu37jBmJaJ0+fTWnpwgjeASn8lRiVXxoZIipQYrmB1pSKlKr6ZgmP6XFCkmpM7cn1PCxCnV4GqxwuD6T6xZFGGZZG6NIeu1E+fry2YUPzhRfa999P0igQRkF7h6MPwJOcpzROy410B6MM4sQpb1GMPKPGoVWrxOYKLTRqQuFc4ib4Qg9gUV3HHPhYEwvO8MtxYNChL/nUmbPQhrv37fNxRnp8eCntzbqbvBjUgZ3WzSODfRmoGAcNwywRXAkjjeLqKHHaQwAnbXtO0j0GjMhdr1QQ3SGfg2CUnJuZzV65Wjl4sLZ6dePppx2lFhtGAcA6eVKywoYZly8Ubsdlo+E5NA+sRGuvufmuY0vt9gsGVtvO1e0K02dSl2g6ZHrKgBEOiIkqbeBHVqxvLv6sScUfmpJBgYSUDuKzfKN7SMaMzIyRfxRwU/1MkMQSIaQDS3CWdIcoOZ0drjHuOOH9wAgBaPwaignPzGZA67B3b23FisaiRe2vfa11990LxNAnP9n+1rc6EGmbNrUfe0y93l6zRoBlRJLDWNd64KlqlyPrI0SrYAlg/ksRm/p4s1aNK7Gq6bpdEm8PZ53JF3oDKyxDojw4p4KFqWAzJGAcG0iycsoePHnm7Or1G/cfOiqveCuoStFZYe7sQ4w+o2dH9JJ2kXOKM0jv8HGKL9hTi4ZRhfi0KnlSkTkeLQvHYS53laXRmjVNkUZhtpG+//Vf453G3vrHf+ysWNFZvry9YUPn1KnGzGyVW2uYazlb2ripC6ynnpJxfDotr2rM90On6nZFl2mrMKtqWYmlmM2r0m5fZeqYro1Vs/sAViUvbUVUrh3ChTQQxjbBdklO2qAFNyLPDBI2BQhO96oADLVonjl34cq161QOr/EoR7fwSgwGOySaVBRKeaDKQ3NhieUBUwxpVGE+rTyx/1L1rQajQ/3ByLfPj4/b4+MV7DMz4PFFkhlhAAyuBb4Rp85RqoccaJrMZJU5sVPJnjjRBdbXvuZ2U1aMWQ9aQ309fgGg+JXATRjUyGmtqK7oqhZ3rcUGVqZZTklaE7o1jH15IJAfBxgSMMlu5FUCRZp04cmhTpw6PTUz++ay5UtXDErzmgpGRABLVGSB6ep1nYhXvONrir1gVOPhn0VmZksjdoEwhiONlG300Y8uUKmJiFq8WKQRogW/+Zu/+Ud/9Ed33XXXh7F/+K6nn34awPr4xz9+5crVZlA9uJ0rtD78YQdYX/86gHX06FG4af5ZD04XYT8dFvjqs2fPQtNVQq4zLpr0UVIPlccxrMcFll1olWalfBfHQUAzG9RANhDG6KWIoGJaXWJmyaHOXbiwfefQtetjKMlif75LsM7NP1nVsK+qJIAbQdIcF/cDpk4MPbJ4zTa9fap6Q7lJ/uo1wKiupNHtwOj++zsvvdTeurV9/nx9mri4ILThZuN2k1JjAAs/QyJPHbcG/BZvCEh5+uGovpdSb7eQ1Rkd7SxZMp9ItJvNgYEBvBkf7prS/E55M/6WNdZdaaqDyJGvkBCXfKP8xfngusk/4WxVhZyIMUkFyEhwI+KAOEJMbAmwKsyABwsL4e0+gLWgCuusUoUIesEKBnXHrLeRnMeApRSGZGeh6MKIBHUtHEbk5VJ/PWwjaSmBCh4Zqe7d21ixovn4ojsFo86FC810muWrJdVRUmDH+VynOQKnhAWjhSwUJEErOVqg6WP33CPy4x/+4R8WL178y7/8y6+//jqe7tu377d+67d+7u1v/97nP98pl69v2PDzb387gPWLv/iLwEGDP46/ePwHf/AHa9eu/ZEf+REmXChPTk4+/PDDr776KkUimMRwx44deCpUqBMTEx/72MeAlV/4hV9429ve9rM/+7O7du2SLsJPfvKTb3/723E0XGWWZFXVvGq4CK1aKa5jWM1AdUoREUJ8iVQmripc2DY7203Uj4xe3bpj5/LBNc++/ErRSyBGCKYcqiWx/sDGRi2EXVJBIwgkOA6l0Su1/fubK1e24Kl95Sutu+66fRg1zpyFscBsIDY1aDjJcsc2KodXRwmwIG5vuZtEO9///veL2ICK/NCHPoSQChYVRuGv/uqvkghptx/+xCfeuv9+vAE5r5/5mZ+Rz9bdenMgCWgbHBwUdD722GPAED4OqfO+973vS1/6Et60bt26p556SoA1NjYGdSwHAYwUM+U73vGO0dFRvIhMBrDLUIsAVqWPsTd1IYPJcxN8qg/jvd8Nt/MkasRqziiEi8OXpWxG7mO7V+BYCixxAzCMUNNDNRi5VIZgdOBAa3Cw/cQTtwWjz3ym/fDDAqMmUgKpNPcVYqYBjDCpNJOuvlKlUo1ZZAcbQ1ThgLZBT2EhdWCBS1CY4pBdxOpiOYG+TrEIxToPmt3paQCLqpq0RgYBlqgzfAXkFuCIx/gLDP38z/88VXL6gCXaWICFNwBMP/dzP8eJJ9o++MEPjsPVqDkEf6JhqwuLOFD8vaSANTOTzFETYknoQxDZAhlurHBDCGVo12eEgYwEIlw3rcXWWrdx86at26ENifk9aPKqpFAk9khzK2HjX7naOHjwjsHotdfaQ0ONixer2ZyM3BEYUawkI80HXaXW784zcHMCLNiISmLJ6urAunTpktg3EBLDw8Mf+tM//dVf/uX13/0uGgs769fXbtwEsGqa2CZgVSoCLA4rlH/yJ38SxxRKC1z5H/qhH8Lbo4GFa3769Omf+qmf+hNtu3btWr3uUJUQm8aCIw6O0LKZaRzXoYgBVTSjKo02IfRTIUJZGvBWmZUjqwYysjAIcgNGQtiXpZEzAfb1vgOHlq1cDfcQnMTEU5DJIrHPxJBwC3C0AmLhotS6MHJ9pduBUWdkhMvqyjgxF0as1PKO4pU6grB8foVsYadSABeuVS/BpBWxr9UpWCzqKN6hgKVfOggDP7AABcku40V06z308Y8/9bnPkfIbGwOwbC+wKi6wxMr+wR/8QXyQwpu1GoytX/mVX8GDjRs3Llq0KAxYeB3vfPe73y1YV/4Ez3GhiAMjzIg41PsCVrNW5rqKipOCrHq6XgcCUy7S6yfT3lAdgp3vcqITiQpeM8OYHP3cxUunzpzD6AchT0eK1b4+5rGNbhtGrV27aucvlCkvbkk9KvAqdBOcvq0o7qtYJQBl07Fv17o7gnfCAqJvCKPFBxY2WO5f/vKXZY03PP74Nz/yEQCrNTn5oz/6o+K71V3jXQELR8NXfOtb31q2bJl4iB/+8Idh1ONNOCy+RZzHJ5988gMf+IAA69d+7dcAKcHTr//6r588eVIev/nmm+IHcMShJhVdRo1Dn9MF88KpFNxir+pZeZqFRR2PVJMsoqiHphA3DQIL7wXwwMOOliAIJ3wGVUeT+/Znn1lcfeCB1uc/v0AMfeQj7S9+sf344whktw4crF+9WuImanzHHAWnSGQyzYBMfQ/NHMvY7cCeJ2kGkelChpDWgVXIQeknZcAYZF+KQZYOAhaOgwV773vfC5grG4tppLNAwLe//W3Y77Cp3/M7v1O5cgXR+VuVyuc/8hEIrampKeFBMICFzwIQX/jCF37pl37pJ37iJx555BF8qdhe995774/92I/B5XzhhRcQOZNgxIEDB378x398586d0qwBbxHwete73rV7924pCXRnYFX8GcP4EQeSWJW0dKsHA0tuU6k2VFrADyab0yBI6RbITSumKJCTgVJNptl1olyvrZewNYeHm/3KJMDoy19uL1rUgXI8fLh+/XqZiGgtqmqaIzMOiHdtI6HbiFEuovXOS0CuJHE9GbHMlFTSrOwfxKIDq5inLiZt0pozuwULKZ6gamnHi9BTIiSAdPnXEn8R1rJb8F6rdZYtoxjpk092OCilJFadWxVIYzabLiVftVvoh3n0HPyUMSq3tI0nipdE22ITm109lX8VQ607BMVwDOvlPoBVnpPytVCJFXQf1xlDyMgisZ8DgCAdEmyrkBSrYKZIwxisatL2rVrVA0Z3393+6lfJulq1qn3kSP3GjTJ1PlmSzKExPhQ4ygpsKwsysYEgCfD6N8klqKfcKJY0sgtNW1OFBW7v02a7O9rQrUFVHGDRtdGSPCCxdOBg91I8/DCBj5WUbpYYQlQxaBp5ZcttiFCjh4x3Gp+SVLQztskLrGa9j4hDszQLO02vtjWBJTDCZUPWDaJIYJTkmm9xmxqNRr8E2u3du01p9IUvtB99FLdpe9+++uhomevCFwYjIROUFKwRBqNqbrdoRLUNxtmkHtCT/Sh3gVUuZtParNsCs/vJZnRz9Oxmc6jqxm6oi1P6+MeZZ9A26LKMFtyosgU3hh4/FS1VWQaa+3IMW5VEvVaRGuUAYMFnAobIUuGIMr7kjszs61y/ri5c47OfBWdFi2R4FZYJx42IQL6vuFGVh9tIFW9BYx1iIZFVJLbGKsYHVmCxfMu2BFgViwZC6WSvCbfRsq9on+TO6fdWbb0Ix5qdxd1r8CinYzeG6DNzYvIo88hVZxzcQlLREn+3CzIKPQBYd5yHHReIbgKr1I1CffjDOzZsNDJTvloiETNEWlxUe3BfTdrb65zUGU3F0OmycbjtYj03OZTxdQ0XWHYpT8SQWhku3iziqq+sl5yPuOgtuMYusM4PUq962sv5E91tG1hlWY7HV2M79Jk1LhheoGPYtvONSoZLNkp3EljIk9aY6h03TIYSwKhqSlGsKuOQzVc1Z/DimrXoNwx0yvolhTJ61MSs8RbwJGQ9JLcdhqSi166XWYqeq6+pwlq5YAALSMYrYRLFT0nq6VvkHBeKB9X1ufbc89zuZ3t52IrxgSsaLebQOSklxZWnWVcLTUVzxIGGHhSCHMNYwMLNxJlRW9gpEVkAhlDSgrQ2nqKrBa9z9NGj0Urf+Y66cDeXLMn7itcWRudKnYnhrdI6dxdPoU6G8Vz6N6OUvqUZ780qWesFbY4DZ6YTOkOJjE2U5oA57tIMnJo2R/PVSTI1Brv+TWHRIiFWMYjv/EGQcIKWavwx92Ls029ExOF2HEO7EOYYDgSqM4rJMkE5qm0QVkDqB0hKUtW4Y13HsYoaq1arC5f/3vcKPlqmhXG5GjwfOS/vvkEiIo1fCls6yERS+l1F18L1hBvqVZo2kLe6wAKGFmBj4fhdYB056lyfe+5JP/X0uQsXqSjKuZVbqnVHceJHH7nsCh6xn4QqIdrYd3jYbsMxbHGNcmDrxwCEoYzdpmboFBIvSYZRmmCUJcYIokmxa2GVslWedyqTQiytMZIswePdUsn03/3dyJWrRplDTCXoNpc6QyKw+7OWYZ3Q0pesLC0pzpHHEjX1ssB5zH893ADpxanGlCGxDO3ZX+Ha5FT7X/91Ht2Fidn20fXFo4/Xt/xl7aWfrj35P8luv/yztc1/UT/3XDM93Gk3Isr3DKLD6MGtclNJQ6W06+g3fL8ZQ+BeapRNYEEUMed0LiuBTma4iS5DkNmWYSYw86HREeqzc91wwz33AMGz2ogRNgiqPUW9LGeOkzTkk1OHne3XL0agyPtPlgJThgZtOspRCr/8J68+W696hJZUDKsB3VJjH2FmhdBlOb3gEgWdHz3dfPN/1J76QQWm0P3pH6rv/3K7klYB0sAgQvRgQNV7InoTi03t9rbtBVat31Q0vtzPLDLQVx+IQXcb7rQ7PxI1T91Wp7k5ogPRCI94ukEmOupD4258J22YtHq8xyDQUl1lCjrCgGK5FLo+NtSMP47VqBakLwXmAdlhbMKnOCiqHIWgWGhWahiFz5cxTRWOUnrQrqQaOz7RG0++vb7jE2grlTpSAzFB9MSlIOambluylKpy5KHZd42y0xjNAcVSORpY9cCmIipC7UXNbdz39KuQQkcDpwLWsWPCBKn97EqY1yPlA1Jx7z8lYfEPtN9Vtbi6jn7HUEa8il42RK/+Wf4xObuc11kCkm6Inb6UHUndpNOH1Mn5SyOaUBYyqgrgJ2uMrqw9+QMLQJW7/0Dj8jIcRxdd2SBDB1JZLYoYo2IqaHyQ1MiPShWogqZbE9buD1hl6XAJBpawa8o14nyAadiG6b5AYOHsHaqn557rAmtwEEJXjwFWaFZn1lgVYaJims2ce0qW9OG4vcsm/ZqsZSApiPL1dIZmpB2J0jORxFFlVJPqqTeM8XwuWy7mdLINJsXPqriAztmsT00Ls6/bzVptw4dC4PI/15f+VnP73a1D/9Q6/M/4i8d4Ba8Hv3/Tn3aaNZn6IT26gXqGrirvMrBNZuDIfXJt7OYrby59c/nKlWvWgcRFhaNQLNRPxR8xaPo7bAcUW7Dudevek8P2yXnsaCWoj7wXuz63bl231QmNmu22PktN6t9lUVz+9wDeLDVzRXosmR3KCiPB8gfQ9dGpNIh6ZpbElQwO5ZMRwZNg8WJI0LJVALD0EGKO+RRVOi9+Sofywa16bdnv+CHSGPz99oXn58szt4K2+fJ0+8JzjcF3B2Br2b/HMaHvoHLzsdnbc9xjJ+d/8Mix9Vu2YlT7ytXrwJgnRfd9RRw6bH36HcOBsHCOBixSglI6rISqaooX914NcFMUknLqyWPHu/b73/89rFWsnu5jSvtrz4URTjqdjEBox6SHVtAgf414qUNhhQHpM7NCwSVThJ1RabNzktPgGFVRJkZ7LOIilKFn6Lfwc4hOl4h/cAjUjBtVgAB7+TtNg+nNf9e+tvpWvK0zubu+9DdNbK18Z6cVYMN4nHc2ZipulwpzLrjjFEulNRs2o5lq+64hTro2+4w45FvlpKpR7gEsIw0iOkgkEG4LcfiZ9j0TmIkT6SKnDl5OPbEDikLmGfMMw4m237vqTAvviqiT3p6s0/bszHzL8ugoUwdRjjLB3g9Fd9RYIlSFC2hYj9SFaF5nCeQbqWDIeZJS/C1o+jTGfUleaI6N9u70Q5SywEBe8wEDE60DX741377V19ZpNff/o4nO9R+c77SpscBtlS5XKqWggeR5lxBVv5jgpVq/acvps+dUb2PMiANyhe3CzXrVElPVCPAOxLScOF5qKXq0no6hnDosjuY//mPXzBoZsZjrXHdbIqiR1CBTV6KYwFK2Dv4n2WjFPSzSlFqrObQG/HEdRF0mXKq0jJjwbPkKsOqKWkJkc5lmMuaNTEsg2wXlMUG3iUXlXTH/UvXw6Wc8aHjmh9vDr95a6Na+tKT29Ns8yvTCy7DBe9dWsO1L1fqu/Q7xLcyoCEdRji5ejXK7miMStvx1MB91SVa9qaG4wHIYEGNY7gIsuTkAw+qjGgfGjh2QGlnNK+GZg54pnbKp1LKyhWUWg3iLzKMH2ZnW43J5JrJSpRBKHbtN6xQKk6lSUIvcvoEy1KQIcCJhY3lGY1dKJeke5KSeJex1hjmsPaaskZgBDoMS7jqH9zAtFHDNUtKQMe1LL926va19+XXDT2xXs9VqtVdQMCtBeXHbLwwPv/LGm6AxO3Hy9KXhy02tU8iTMbTBwJbulJPtcoJ4/YoT7eJ4s5SQEIxyP41U9EDMkhL5WMmdAup/gzLblXMkUrf4xptdifXSS7ix0l5mczWnVJXOBZrD9KJGSMmotQyGdF0ak8eneXPC+a7vac25k2mruvPrnfZueb5Xsydoch2PqVa0g8aZIyhA8XRdAx78yq07sRk6sb71LunkiS6vkCJ3cdtBtX324kWwTT3/8itoe2lrbf7UCmbn26XZVuEm7dZ0o5xGxLhes8OKmoyIQ1xgycf0ucgCIJ1JW9n1qpyNBNLOXV2J9c1vCk+wfgaKJ7Jn7aVBm1kSnrJqTVjvILAqXjIg3ZuTbIERq/Y0onnlri7Y+fhVHcEs4WqqOgN3XZojVQaqqFzdmvEs/1v/T992Vbi9BdvfA9lyIlpoyeV1JXTt+s3xJa+/iRki6KSSglK0EbmR0joJJLvC1kGs4LnBkBMLWCKNaIKSW/8UJld0eg+59HOXLuuJHWRWU96BUKIEox3DLGd8jVMvcbxAdzV0iSXjCyMGa+nBYnYzPZvBK6S/WUJBjrnJPWVUs8r2slkniL77Q/fqa9+5ueXWnds6NzZ6FOKRB6ItLaG1V0SPMjdpZPTK1evXDxw+snHrNiyNrhD72o0ah4GYZbvJpIo5957eJmaW3OUwDJuf/GRXG4L4BYRdumPIwI0YUKC0pMINm0oVoVTQPdOSV80bNHkR3JYqQq02yzs7ST8UjWAIoXJUclEOAj1Ye/qH9XjVrTu9NVa+S8sn/jDcwwiSMwnNyAUs8nTM88PDoF+8OjYGo1O6M+oLHnvhtUQHehrjatS4w1Xfq1hWTf6VIR/4KfX77jMSO3qZQ4mrMXWObrfHutueoFICIhWIqaxUkei5t9yqaHCRR9Cf6tAJTFjpYlV3DA2c6SMhZIirO/zSamSveWz2C88LGtCh9Z73vOffLWj73d/93WeffbZrxZ9f7NGG+ZuB2lDN4RIThYZ588UBZ/HQnn2r1204ePRYlVt6FkxRWSkVmtZsi6z7KewDYYpPG1hakvGF0mNY1IY0BebIlPkl6py445973pvYqelhYolPKtPNna4QnBghoeWut6hC4+RrHjR0G0j8RYVltxzIn4qWTTfYjchy3uvYGkJCLgV1zQ8v1TM2iKELGoCqc+fOyeMLFy6AkUbayPAX/YMR/3T58uVTp0799m//9tWrV924/Iye82mMDiK6EXirK1NVsWniAcbpoKMYfw8dOTo+PhEHWCh/aIL8vUT8720wdRenOgXeSwlhzxJfcoDyGBxcFlHpsC265PpVpzi6Lhz5Mt2ew+VJ1U3g59BSAxAdqlZvYgd0ZHD+9XMVjz1+y4A+A9E/plpXhUp7+gYBkwupvMJgYHmdSh1n1LFDnZ8Ucw2rpyOej6HPds32pb+lxAwED4xj9VSXQPpjvActzuqpeoyeVXSodt+29De68B36rN/MKmjGhtwwkPJYO5l8gd8yevX6a28t3bl7jxdYda6Jr4LKtlkttIvTXQCBshv879VsRKxrQKdEk3AOZCkRZmazUgImO+cKLdspcrdESUX0J+nqPHH0WNd+//zn/Y5hzDpMy6zNqMtcCH3TrbcSkygHkjThg2Ruq64pVoVGEXPRneIp4zZpbVgyZZ2BKBJfsAL8TSfQ0EEuTy15c8dHdWDdf//9e/bsAefC9u3bP/e5z0HMiFj6+7//+61bt+J1/Ot9992HDmZQBC5ZsuSZZ575/d93TDRw9qG/uRt32P6RLrBWvMsfdDBmeYgEAbBw6lhFcLds2zk0tHuvHnGo2+VWaRrpmlYl07YzSN30lT0kYPFcDeoJTqkULF9lalmxLA1YzvTlnG8UWxiwnAY6hHcnJo3ETjJlOIbZnl1TxnDhsKS4Zy4Nq7/AqjdJOql/RXZGH5SnNHKRWnZzIpUFTNKCbUzODjxnmlP/7P+qha++pgNrYZvIOUDnD//wDyGZnBbng1/pFp0+/1PSlu2vbAsYOqSZ27hB1m/eqoLvzUoPgdQbWHoRldjLWgNnBrej2zlOmpGb06vR3Eb6Con6QNSz+cUv6okd3O16R4rQ1yqrXypnVJcLniL7JhJUuMuLQYU9fm+Oo5c5AwEqm1ni8mIxHGUygAarrDoTxV6R57xWMHOu9+cjsD89M4slR9lnF1hH77t9YEHOHT58GMIMhBEjIyMOsI7e15VYi39UGB8iimwFVUKvj/MHB/GylavAFntxeLjhlvu1qvnbQRUBSxUpqKkQKigqFp9Y1g4DPSdyXTs9qwbiAAdC3aS0idziEv6hFN6//VvXzNq+XYYa6IRvOotL1icRRZo6kz9cgVH1FhYrDabbRnr6iGZda5u6AcR8lDGzevhKqCu7Ai+oTlLNWFOHRf4ZNJn4AcSe8Pz/HiGx7rnnnkcffRR/Y6Lqne9857/wBk4RPIUODZBYL/1MoMTSnS2VLpPUIUQGmkSocaNbRNpo2eXbBZa/fUWv1zMmgQmw0KbDBC9lubvD7G5BqnOvMwGGSuzQHCjdMWRpkQ9XsobxpCdbjLSS6+qX1IglmUhTcF0K+SKL2/Ml+ahXPOshVsgn18/NCleKJwbI5SjSoeV2xpKnSdU4c5TkJuKP5f8hzMZ68MEHH3/8cVhO4LgCVuKgasWKFYt4wwNgazmYasTG2nZXF1jLfy8wsUMqSKsmEvNGPEQ1TBXAQnSUWv2aLRhZtwussFIF4eb3+2VSlRE9bkl3Rhxyjr179cROnVYxr8cno71CnEwgQQBzMGf00k2Bpko6Faj1oazmBua9MztFpeqCU3f9lE1GP4RVqhpbnHcmEpb9Ewk5JpxE+hq15PV9/+D3Cs+cOQOgPP/882+99Rbwgb943BNYAOITTzxx9913w2zHgwceeACf8nuF9t4vBQbfmXE+qQNLhTQBpqmZGTSf7Tt4aN+hI2Cqg6UFhxAlDHdAYgVGsyj3vKDWP31mmFPlMqwldsDv22zpjqHcPYHiKufazoFE3Ipmw/BPRUeXfU39Fk/91K17XbtJF1Agv6GcAHzJUjhRgK5DWRjU66OrPHGs0pQSWiBPU8AC6WOcIDvwBFRBdeJBt760NKnHsegbazWVt5XbQJVN51xtKAE8ue2HR0YG165DSmf/oSOXLo8KbThCDWjZ+L4AK3rsdg9ZxSuBoYOTU9NIqXHtb9LTsUNsVynv4LG0MchQiSJx0IQWXugM9H+V0gNDgsqZ+4HFjYTFsHwi6T5U8BHaRDBX9fCEBMbidOqKMUdsWJWUN/L+XDcVQx32i6AKgRKJNfTcDh06JKoQ9rsWefdUeqEMASevM1kEdvrL/ZOj4JwNYB05RpMTW9So3FCWVqtWuGPAMmpH4zNSSCEo8k3whgAmfQeC8PugvtsPPtgF1tGjGSb+UxMDxR9WMf0w4gPH8mM0AAecGq8HpgI57lXyyzmjak83mwAplVn3TzW3Gb5hE28VfZLF/OEFNhsgUOwl/4eWK3z39yFX+HtarOEnheQtdou9xWxQpfHJSWm89rREV+8QsPJax7C/F8rfzisMpcSXPD2jYARgpXiIMquYuk7ORqyhWmKHqTEc7z0bMm83TMMqcCgVydHkvPiqab5XIXICezhz3iQ8RzHKzvB3z9CXrFHGI7Vs0tgj9JPCDSjWgmQjuGTZlpmaQkZaP73IU91wY9MdRFX7+jr94PbJx5sx6kiVLOcx9xiKOY/LCBvr4JEjV6/f6Iay+um1jwKWItnRgaUwl6KhNChhp1JuCCFXGs2lqGm9wEM4MBC2HZOKDYkd7n21lIaKQzKmFdSXFSy0QakW84UI/W5drG+/5jKSM05XD4Ojps02Uh6AnqaUghnxK6HpRGkbo8vUL6JyTTCz18t6C2H9zV9DHdUdKppp1t/4Vb2ItFMvx7aJLfGaIVtRg3Xp8uXT585DJ761YiUCiDICqE9gFXjIb6JTmuoUxzuFawOidKSKQcxn7j9BdyUR7Y1PTI/dmMR+cxzSCCUwGUrAEZlz3xx/OhUbEjtUo6xppQjKHieoxs3yOgOdhDSrWtl72tWMytbxu2xcR2VpdIzdgdMcPigZyQNdYuEk9YrnEted6mpa6jNLrKMFl6Rfjjygy5Xm/i/emQrSfV/wlL2feAQXOb5R7EYcsro4WLFmLWqzHI7dejUSRtlOJdkpTXes8XbhWid3uZMfaRevt62JujVbs0sDkvWTYWsA08TkzI2bk5NTeOYQJ0uP7AIY2KjMjeUt9x5VW+Cw0xI7Teb90c1qFTVQ85j1wCOXtJclVSc3gyhQPRouk8N0t85oHam60V3d/NcYZizDEzQmu8r7lSMpVU2e+kzE1bQmH2knJKGlVWV9X2reF/9Ip1ltttrRU/t0z0ZuOVwEQAhJvcF1G15ftmJw/Yac69w4LRV2vlOe7ZSnO+WZNsEIBe/X2vkr7cJYy5pE5XujkgU9nRSackLWkkL2ASg06DhxRXGHL4Bu1DPgqtnkYU+WLB4gi2ghzVTKwElMtr/0pa6Zdfmy0byqMCSGs0IAj4/ngCyzHdue8EFJD0RR6XrRU3BsRAcCu2GVxMrz6ByjPV8f5apcVMXsbSS23cl4nm8hlsOJPV6Gj7eh0+Y2unReNrp0WtM0pUHntw2DFHg6MZkGv0OAJf23DRpLgz8Oj4MWf7faheut4o2WNdUsJxuVXMMuqSEaqmORPSciJi5IzRzbIgMLZhwVVkjp2yeyKJF5MiNLi0ipWheYwvXvPa4ndow2G5VW8vsvobu31Jp6rzX16qY1LcafpVfz6bx7CkzGtxuMWaIctXr5ul4xoerY/JX7Uk5e3/8lo1eHdGK/9lanaWhAUoIHvsLxi6ZexCHJVuMuwkgRFEPp17yoMYgKAU5g2krR5QuXp3QiSXl32WXQ1+dF0pCmmBhi+i8HQzLrFqlW6aJKMd9wLqSyNJV27m/InCrmACiJ9eKLVAdWqkQXz/h57rykF1UdSbj3cEEjGAMVj3K388fL4aYvhnJf9LWJoLnW56UH9Ne3W/bq95m9pm/8CndCz8fuhP4N8wir34P10QvVS8SEk+GbXPL6WZkqClSpQqCwweZYaPnhxPjPZUVFl9mbYns8mtXwh2xh/y8AGHkAA+4dvheOeTCjnxwozwkmmnoyO6d2Z8IR8yzEDHFV2FeC+WPtP6AndmgojSZv/EqE25UiKHFrkik3Sv8iaMcMVWVAueCrvjeehkXXDDYs/6+gmh8q/G3ZGwMYQVC33j7/rB6X93A3lCZRf6zHqzRU/Wdw/0lgM5imIUe6jwbValFDA1gSC8QNLPxKQmPO46skclIPmOXBc5Bp8ig4sImxgFnDIOp4XpCgiIBlc9EIAIl3TE7PMejgFaZYJs0xbQcl7aXNcyGReI4n4YQyV67qiZ0qd9zrwBI94kakMqqjWotAli23KE+NZvWWduX8ssRfgWiqNk1EGW82JFY0zZrlkj074Rs3SSDfCKmPKKS941PhbDO/gYxy6+BXiW3m4Ffx2C+iunAc+jRRSoajqte8e4e3R3whKe5wh+3Y+jtpKjuMZsQKUiRlkhRgKnLwLyrANEANcSiHIm1GTDdUmkJ7leeDpXW2D7GiYgoq/X5V6AFK9cROY2JCzxjq83xV+FsarWQ5eWK7pbNPS5xCT8tkc/meFYgKLv7eEANJhsFk8J36qzlw38sbnDpB75dSCI07YRrXN8OPWzg/1uIfaY3voIhDjFYtKTUTpaZyGjJPz5BhOH+in61hlmOGBxMjBp4WYABHFBzQRtr03AZy/Wx+ClAPhpj+BUilSU/gVmB6BbnQbm17uvUv/6IndoDU6DGcouxU2ksBqxsXdecrSXDS6pXQ0KHjt4cMYBk9HQZcAoEl6tU4jm69ybibTqNaP/C1hTD6Hfj6fNMmdGrlU7qbpjDkDFUoyJTYgBFJogHV+3HCYggBdSRcb28LAJaEIpgCJGCDZpUscUbyJ2kSbCn6m+pmPnN5o8BLpCuw31yypAuslSvxWa3o3upHu1pSD6MLlUDeLH+jR3RnrAEsI/og0ZAwZ56nfGUsHuMXnUiAYKBVrFn18y/aL769N6Re+unmxSWdRtlh2FfSiLOTeVfzqriM36JyRBclPJzbVS4dj+LBRJLa7YSZugY6MT1T3CcAWDzTNi9S3dzodVuseD2y0LM5UcQJgm/2tm16YgdaUuVnImjfBcF+TOR6JRn9OIjuizQcQ0X7ZlTER5CtZ7nnLM6AbUplsrXbbtbrr//fUVLq9f9rvt0QSEkaPuuQ1LkOv13ziy6FuaxbQyYYgkWOOFH0jK242RQaAMALZ5VkbvdcwhnJHgAs4nnidvVAiYXfAEhlYo8ScakQWE9Z5dL5C3piByeEk9Br6+TCZfhCYA05pU3c6H4V7E/nBUggr1XkBK4ifVgdvnI+8i3SLEV56KIVKOSoRaBcVpQhPcPfqoAbegfjQ2rP/XggquzFPwq+F2T0dNwEhvSk9Y1JSjLSFw4JWveWAtzmBuseuKQ5yE7ezxnm5Wf/HwiSSgUZyZrXqPq6wOrFGRm4OaPMKnYW9rs26blKtmHW07zKo3bjlILpnbHRpnr8kIGf31bySHqwR6pr9KS4U/9EsqqmOl3DOk10Th6VxSL24uJEEOPtDzSyV3UjXSocYU5nMo5XBG/q5sQUUxNSyOAOwkhGSVBDTb7ALiEPSA2CUcAAgSArKksDAdCURwn8jHLWpA7RHUnqEUg9yWokjIlTgf7UEzuNCxdg43u71wvx/U19JFME+PpyY438oNMIrrc+uwPfxJwSkcaFKMw0yRaWSF/+9gzGZyOYlA7cqICRzOsyD3lrTe41gNWZ2ttm4lYhSoIZ65IYspFZrWJC0bLB1cdOntq+azd4Y8DNJ9bbwmBUZ70GpAJAgBGPa2Y5Q01vtb6CGgHAKnAihfL8pRJMKdkhSOaYQY/b7YvSr5rgyj7UY6XdApsQVUjzQlw2rDRminbNrG3bcHA1ADzmKBgnpOQqoAgFp2irDOERoRDVaCeVV/ZXcWW4XEd2nDPX+NZlt7gVUdxVAQ/P1EipKJ1UIFZ8PhoKVSjfd+LJbgZw8PO3qLuB0ixUtFiri33NTfSWoOHa2Ngby1dcH7sJCFweubJ2w8aYEqtrHmFqJDjoObKQzTvTtG27tmASB3deIQcFFC2dAIt5O+jWkwCpsr1wHhCEABP2BNGQZgVYPU14FVbAxOjWmjV6YgdfrhOgBaiPXJ7Y+lMpCWGkvUW3/ui54db5gUXZca0AXIw5BSaxe6QYmiuMq37uBqGQVNjC28WGlkC2qgyOdPWJLLg0OV06ery8Zm316Wfs++9vPvAA5tY33/gzyiS+9id43Dl1SkCAN1++chWiavTaNfANrdu0RQaWYJucnkbF+qGjx/GFg2vWNxrNcPOItE2Wcy+QRlk1dTmGNBITk0c8l9zsKmV79C4BHEbFLwZEZzMXQ1IupbALS7fdHE9eoAhWKgUMSUoHD8RQwS6J556qEB8sS511rlA/1u24b997L5ZAOYZi/NIQh2RKfM9ZmaPl8nX7yxOMRJBhfQeGNLlWkQhoM+wc8PiIpNxCMntSdXCohJIY74oGwjXdLKdV2tVoEmgIHnuOV2/erB86XB9cVV/0ROOrX21+9KP+icbtRx65VSw0l370ViHffuihsUVPnDl/fnxicve+g6fPXzh24uS+Q4cxAhelnsOXRwQxN8fHgeI1GzfjFM6evzA+OS3j7NnthyAgvUbRTjaPilaPAfLK2BWPUn5SsRgaCcqJG5dnJeBoaIrdDyhqBmYOovin3OVkcVOSxwm+i7cvQgLlyIIqSCw89g5XFuc2p78ozcFCXYQzraDjXqNiK7DFoPQgPqeiYHpFSoTmMsxt/am/dF2vnYcBgS+XjJiMfBUmUqEwlfy30FW4MiyvOjvCNJoDPlzeyyOVXUPl116vfve7DTD8fuQjMYeut595BpPH288917zrrrGdu19bumJmdnbH0F4cFlrv/KXhi5dH8KWnz54XOQQk7TlwCC1cuhUIrUaKrSBj4GvRGR6i/uCMC1NmOvOI1Ar2sEnYJan6WgEcYJWIG9jwDfPSbaJekShAgYbxJVkx0V2e0ya2SRV80pml2+XrkGpmyTcDrPiEntgpX7ueYdM+Io4VkUcyilsUtW5g6NyMZ7pcOtRkQVaUw8gtP9wh3mEZJvJbJ1HyrA2c5YsXy1u3VpYssR96qPF3f4dKxpgwMvbmX324+ulP2y+/PP7Id86sWXvk+EnU301Oz+7cvQ9fNHptjLO6quKjjtLGPq0fZ4aNnh/THJdMyjdFoacLZRUDegsGVJVSUAi+qANLoRgaY4rrlI1B8FHhhjIxVIttAcmsd+zU9++XOShGQWZfv00N7VWcC8orVI+d38gnSrcB2nG5KJ7LtqycxsJdFHkeIo1QU1EdvlzdsbP6yqv2t799WzC6667aZz+X/PrXiy++dH3JKxNDe3BaFy+PAj2wqDCI78DRY2A1lnLnBdvRDvl7OWrooSL2FeqXmLOoHf8maD7UgGGO6JuYzKbPCF1D9CGpvuJYetcUgrN6x05r+XJVSlpcaH+sQEpC0JY79U4zw/MiX/WdbKxslgN2VZ5dGA6jc+erW7baL7xYe+CBxt/+7cIwRLGVu+5qfPGLpX995OpD37766qtrnnhycHAVFNnZC5dk5PtsIrUA3JDE9c4NEFYVETz9thz7Bwr17ndnftpmvQauZQwDa9dKbWmx797N2iYrkdC8RQmCxwlcBQLLabFCKjqVaQ0Nda/4d78LC0zsgFK8Wdk+ieUk7WV6dpfeUrz9NGqG5pzalVJJ1RkFr1MiaZ88VV2/3n5mce0b9zY//vEFw6j1yU+2v/GN9uLF7Q0bkG7vTE2tHFwNmxo9MNdv3sSlGLs5ATupGsOxRwCgqKk8p5izXOaMuxVmHvSl0YwsVh/AgslUzAVzNwQCC4uBcolEMqUrwuih8G6huoovd3e3VFw6u/P1kRE9sQP5J8nESrUa8y6h0XDpjBsrSanUiqBKXP2SUxsS4gHBXJ2ctg8fqa5aVV20qP61rzU/9rGFw+iv/7p9//3gOyEYnTrVSSbntZwu8IScDGxwlT/uJYRqBrBU43ilascUPAtuZO8LkfiWwPnkoaqQB0pR6MxvvPujCUzUnpTAgArWM8Lkg1nJBbg0ycQdqCd2wPEgPmN07taZgMLxDrXPsFtqMS12qFWEHTbK2Fht/3576TL7sccIRnffvXAYfeYz7YcfJhht3dq5cGGeC06M2CORzEIroSWTC0DgQQJVCDKVo6v4nUtU9BpJdRQW9zWhPb6RFAisvj5eLRcCG1aDgUUWHAqZc57JXinfUEmVIZapMuGjeHTuDdRZF/XETvnkyZxLJyQmAgX6OULGe0rKo6elhhUCicIP1O+uDwL2GkZ5e2S0unOX/eqr9qOPwrJZsH2NMEH7C19oP/poZ9my9tAQWONQcWsEHiFfZNYQbkM2ISiOm3VigRWuV0EONAVgUXlCkKlr7KqLRnslp82A+P5u3J+cii/wrHw2uBPa6IXqWlQFOJIVlSuUMKkfNDKdtmeBg/Q+OJS9cAy1xI69YUN3vhKF+wGpBOeqJDHmrE+YNEKowD5/obp1a+1Ftq/hpi1YFN19d/srX2k/8QRg1Nm/vzM2Ni/Dm7XGJBFFWb4ayD9QZtZN3/mJJPXSVkQsASw91hq2Q2TonJcitFQVw/dDoxnaLX7QgX59KhWlCg0zSwErqUUcaFpawHiBTLS4EsUqdFauY5hsr13b9bqffVZPRffYQXUv9vXixQSjT33qDtrX81qijQqlmk0HSW4klqsY8ghFlKvVvvKywCIOBWDJfcp6u1Zxq6aoSJrD3ILLsnAXhh2qaMWMwgShLSd6oKeX7WdH89PAQIeAvrAHsAxtSDF3AZZW+Z4k/ejG0938oBqHGUzglkWePJF2GJTrKhXdOnmqu8Df+AY5hv57HVJqyrGvyU27997mPfd8P+xrvWAN0gIJkOkZaF4uZsxk3UrLEk/PshcWESDOIPSCc6WeVtyQVdHXMg3eLunl/KfOnVu2et3KNRu2D+3btfdA1Fyg2GYWW6hEldATlH5gwQBB3gtgmpicAp4mpmSMWipYFfqTvsrG4tRSxeWmcoEl7e1uCYAKF3GjGDp5ZmWCl/j58kpSSzM7qeh0pg4XSevYgeaDWV6+dq26b39FuWlB2bTbt68ljyYwyhCMcK2dmljyMfJSblTV6EZoOghjotBXWNKRRiwnhCkYB5Hyh7CEnZ7tfum1NxCIXrVh87XrExu27lLv90/Z8COASyoCp78WY8ai/fVIk1MzQBXkCTP+dwVqIMvDgCEwFbYkaQOfXU3ppdk1zHIrqi3jBrsTzL8rDSAknKj6Il9gPy2w+BoXCLlsYqfREjvN24hfk3395S+3Fy1CEX3n0KHOjRvzWk2SU4VNpAZsXCcERvi/U0TrpmUjiNQqwqWTCxmhY8CoS6aq6s/rdZkDhYNQB1upHO4PdidibN21+9yFS8tWrd21Zx+ygX6e5jBhg6GpsieCOl96et9hfeG4boE3QzCwjM8DPTL/24hTy46rleEQl0SuValWxqGFrfe6g6n0MUEpxRTVemuJnTtlX0O5waXnMldLymcZRhCaGUnKCndaX4qM2oLLZQGWQNCAkYyslthrxMFxI+IgdG5BxF3dnnc3HgE9fODwMcyal+W0NbroHkE+yqzjPRAKuUCeGRk92VeYlEd7ZkMiggGERwP6SOaC0/qcyPDERO7UdjDkNNKz3OIHXKAa4gRxKQ7qLqhfFsuJ2BYqPaDXcRvrBWuexE6EYXTffZ0XXmhv2tQ5c8YIPDINiVOFTeWzDCPqze0WG/Vd+hhoHhVphElbikKzilApImwWMj6e6DdYLEXQTCr2BID42MnTVDBz7oJQAEtFZE95owgvApvh5A7o6QGoFIsUATClXjaQXxhmj5/+b4CDBRmJYUrXDSQQ7RzTJCOr2sNcpe79CtG8QCJQLWIixVVbtLJU12uj0TGYd6QzOqqHSR3D6KGHOq+8giG/nUuX5vN5k82mpcEoleGmbQpsiWHEfFj12yx9lDYpTjhaqt8aVotosVwuv+CvKDDfJK5GNlyl6iypM3PJ5WvW4TaRb7TcRH7PVjlFxBIzmhq2YRXJSJ+cxl8YWEWvBmcKlhzzstZatXIAsCQrAiBxBz71vIbJczIWmA687YoNVZdIvNdMyIbcRR/9Q6Oj8NHae/Z0rlyZZ1aWrpsGRiQus+QhPyDTIhjhWkOp4buEkRxnYTDf97drHZsF7pgQbpoql8cQrw+HNCtU6iPAqvS8zaIjDsR4i4hDOLB0llQ8Xrd5y7lLl/cdPsrdzNWsCiP36suV2ZwxAxNhGyU2ZuYg/Atc+G+QAhMvtSsaA4Al9c09RRHEV5G4yzsRdCi331okDSGQQYSh2QS5mEllYnfVLt5sfG8cDFEhW6UiGHLT1pRPFBgReT7DyNgRhqCOLpsiBXoH9gJ2LoEqScShFn6crNa2hET1uQvDqIAQCZFyGVB6OoZ3ZMPNrP/eRs1GWhB7TkZD6ExatZIJLJ3WCDIn7/DRZKHOZlk8QKXJ5ZBVHJ3tLNpW/9jz9gcfrfzxv1U/vcResq+RLc/3hS1O8hNFW4PpAGDHM3sJ3RoFmjFGr/uJCerM4QT5hC+6ac18bOfXsMv3BhfXlhzjWlW+C4xE6AbCSPY6f5zL+lBHUBHRhTPSO9UQ7MRBwoSlQ7xBzU4VGULGlS2OY4hbtBou+aSNTB7fnJx6a3DN5h1D5y5d4nF22Tui4+K3m7tXvtaqd6FDNOPec/YTlg6kuIGVozhkVeWZHBY/S2/2cBaveeuRjfX3/mvlPb79/32ssu18SwfWqm276y6HBJTki0tXD27dvWrjZpqLWbElxCX5wTc3bt91+BidHy/362s3l3mSIAxmYrJvOB0sWFy7Xi9TjJG+6HJu7HdX/jl2Bazzo9cee+nNXUdOWE5zrBUNIxzw6LlLa3cdOD18pem+SM4Umy8wpyQD4+46ypvy/grxZLSMOnFFSuOvyLWZF050azm8cE8fs7hu89YCh+PXbdwk9bd91Tjc/laDqK75nD6MUS0nm7Wyds/XO7Y5Vq7Ss1lWUPV3r9kCo3sHa3uGW9O5zpW5zuazrc+9Sq9/9hVY6fNCjQ/F8cxbq7LMPYm7/sjpc08seQtffxYDpkavenyKcuW7Ly97c8N2lgHNi1evf/qb/wr3EWuHeTfAg74fOXvxxVUboe/9wIKifPbNlRPTc6ls/pm31kAUYv2u35xQdpJ/33/y3OJl62yOfDq2FJMm4sFcMjM+NetCrX5m+Ap+C/6JpVRzKkH6iJxELlhA+OLyteuQ91LoTGJmek5VDOM+JaoW25ZxQPCS8BGxSkOrr+iOcD6OomQY75C023cNGRLx/wdUVcqllh1EbltOUHN2qcv02bAr82BN1oHVU205Uz031oGeD3y3cvCKyW7Ymb+15WyzWqd3JjNO8OOZt1YjaEGr1O5Qoe31G/mgymi84ezItdEbk3gPrPFXVm8CsG5Oz16bmD49fHX05sSyTTtGxsYnZ5O4lGdHru4+egrpAAbWDR1YWNT7vvf0jckZcSrwF8bZ+i3bV23aQQUaBWvpxh17jp95cdVmxB8avL21aecLKzaMT89h2XCa63ftW/zWagDi2tj4nkPHNu7Yfer8JYi8r3736SdeW75iy+6pRBqIf3XVxpXb9+4+dgYf2Xf8LP6+sXrDopde27Z7PwiBAc3tB44Mbt/3xsadsCs2DO1/4vWVOw8eO3Z+WNKCEF0M0IZ/qrRnzKIW6AK0Dhw5hgmog2vWjo1PRJPz9FkekwucFCn533QqEcqajOCCXRRgQSo169W2NdMpzfYNLNhVogF3XmyKviPCfkrsIAlfVeBDRvDAiTO4cJBYJy4Mz/sIEKWSX0Fq3vcOoBAfHJ+e3Y9VvT7e4HHIgdtI3gMscUWN92CZV2wZOnDy3JEzFzbtOQgQPP76qis3p0QUrdm5//HXBnfsP3rm0iie3vv4c1NzSQlWHTt9ds2W7YNbdg1fvbFk1Ua8uGnf0X0nz722etOeIyeUhHtpkP5p7bahjbv2iP7dfuDopqH9BaJ5pf2xF16FpYF90ctvyNRjrJlo2Ly3A9Zw4/V/RYZg5Or1E2fObd6+k2YXdEdilxYAJqmRJBd7Zg5xhGQq7a9rmJqaymeTbTt8MgW4uO18G9hyX5kvp+atqbjAarG8wQp9bwspu0+9VJ1nVPFAZdu/kLqb5gymOnOi9PRjhQe/jr+NMyed+XrE1O24Ap3Sxfy5LzbP/2Xl7D3WjdduzdPrQN3VCWcu95kb9ad2NL45WFmyp5wvN9/an990plmszhvA4m/t1Pbusv7l65nP3lN88OuFIwflu46fv3zu8tWDJ8/hS18Y3DQ+kxSbCVLkjQ07YCSJBbZs005RcFt27kZAcujAkSdeXQphuXH3Aby48/CpoaNnVmzaeejUWQWsp99azW5j68yF4e179oNA4cT5SzsPHlXA+s6zSwRYDz35Ao80Fz7SWs+0o547gkuOovgN23acuzQM0yLtjtOOWcYtzS/SpwmGbEn1gG5dKB69LHNZZJdnZmZaPYedELDMxPM8hJY2fmfA7JSlkaoFYSklAu02rTR8QABr6eEGywYqru1Nb49Skwe+nn7vv9f3zH1fadm1rnwau7+1522t3T+o9vbJd7aqBKl53r+3paq7CHc/6zy9muiYwGq1st/4oufr3vcf8i88xTBtwPAauT6O5Vy98wCUHQ9qLSGXuvPwSXGvYDlNs7gCyP7l0ce37963cWjf6m27J2cTR89ewuuXr08cOn0BWnXz7gO7Dx3Fjgu1kaXgxZGrew4dGbk2dvjEaVyZi6PXjpw6u23vQbiE63cMbRvas2n7ru17D/FQDCe6IZGLqHos5kKSxxBXh46duHD5MmcR6qq+KA6lu9RLUdIwQbmIgq99XnjtqaWPp41AUDWKid5zKED+7hvsO19OwvDqAouaM8kxTHLEKOvk08pOzktWDZEFLOee4aasE+4VPMiXO1hp/15nG6z46IOEpD/+j+VXnq3t3l587onMf3k3XrG+97CDhKnnCEx73tYe+Uxnbln75ndaB36asfV7IrdWHG0IjB5YU191rPGdjfZ7H6mEAavyxksEpg+8K/fi0/XzZ6rrBzP/9T/R1w1tF0UpYgn2gLJOoK/rbggb15f8O9JWRAt94vQZLAa9X8IeLJ+yeYvK9LL588NXyCJstWFQ8uv5A8dO3hifdARJpXri7IUUT2O0qfTlAjqYuXOzLG1wknYslcoRWcW81tc0Pjk1uH4T2BlOcWIHhSuCuWo8x1A4CcLiujIXF0NHUtygWy+mmrmJWDNOfIpyHmPoNLQNQEJmfWkKaf/FbS1K7UOLSFRsP084q3HFBR5kSp33BIUeCtX59vQkBEb6/e+oD1/oGlhHDrAgeUd7ZuoWcjMH3k4wmn6xK8Aq11r7fgwvzidXQ1X+t8fpS1/dX1dvgBIMBlank/2z9+PglRVviCaunzya/9SH8Ur+b+4S1QyFRdGWejNOOxQ3hFHE3yDf5o2kXbnsiD1pt+Kgl829e/J6mTditJaRYBIB51noVerXpVR0NaL4Xaoi5DHE1ei162fOD2/ZuRcSB2lQKS61YxCo8MjIWS4RsCWQZiw0zhUxaI6hV5jZq9YsejRauDbM+YCVmXdHHNql/ACytv5bR11QUYVfeINU4aKtNVGFRR7R3urAqG+pfdcFEjAQKs32LXvjalrUL31GRq7btmOQ5T7+53jd3rR23jpB4mr/TwrFeaPhBNNblz6K1zsjn7k8Q6iFx1Cpd4TFlfFx679+r+oHFpAq6q85dr26Zrl8S/oDv1d8+JuNy5f00Pyd9dJlREp8I1r40xFxEOkYwTQupRPy+PLVa2s2bHrp1dfPX7osJc4yfSPwtwjzBdVMzyU0i2omo5U5yDRGlqBClZjWBwQ1ytl4wEobgSsCFrRhrVgt5cvFXACwdBkLZ5K00hGKNfzJ96qFSkdwgDvRMKrWnqD3IBBPimn5a1ja4rfvJUw0m1waQMjIf/HTJFeWvzafGyJxdfRXcIkbXP6L5CN+bfva1+j1i3918kZLvlHFPwV5n3jB9gOrNX7DseH++D/ib/bP/qj8ynPtTEp5Eg0tjHk7M2P1bWxi6vDZS0fPDZ+5fO3C6LW+emA44tCMKJ5hx7CgRR9KkzNzI1euCfmFGm4lXew8HyCpYKT2Sa7wpPFsXvodaeIVlhmhhIGfWHFDr+AYiPIH1V7J+IRWoVNKlAoZoIqABQvdBJYmY8tsTlVqnT9/kkTF11fY4s9xo1xb5Bm2cq3zl0/TG1YfZ3W5bwgLnLvnz27xG+Rt8zU786H34PXagd3z9k02sP6XRmVGjB7AC5epfeoPSGKNfQvRV9F6iUJbvo6CVY15BNL8wJoHV/EHfk8Un71t47waWMqfMuY7LsxL16kAoVk27D70radf/caTS9T+0uotiVSsBmIJZUVHHMR+V4+Rej9+6syxU2emZhNczpRVwJIRjdJpp/aI8Q7CgWsoRFnxbnCxlIwhsTD9y5ztW7cQxZjJZlL1mj0A486nCj0ytsnBpKPXmu9j2xlqcSzpiZGen2h+8kVCFcRJrUFypVOtQGxgpUuLv3dLtBiO+ch9JE7+/I+AMELbqf9EwuncH9cqVO6MHGDr5ncZbT88X7mMN0D44Zj3rbabbRE8t5CjDDPei/d/FQcv/NPnFarsrRuA7DrHOHRsVSp9p2/TnICSolmY6o+8tEyHlNr/7bXBONjiUR3VnsAyqlUhV0au3Rjaf2QBjqGPgdcO+rpuGrRpJeIMKBTFZ+wgLxTTfADat+KzsYTLRlH2iMAYulgX9xD7375cfWRD7YE1tXued/I8f/V09WaqpaRLee8uGO+EpL/4YP6LnwGeSFu9/x21w/scDVW5DBuLLa3/rX36Pa3D/6dEHDo3H5I3DE+3RT595NnqQ+vrn3rJVv6BH1ituVn5iuxdf1J88J9zn/gLUY6VlW+YwKpW+wxPZ6lDP+uMDFm7+6DA6N6nXlm2ZTeippv3H334xaXy4pI1W+NYWpKKLhAnQD1OjTJS0ctXr9u8YyfiHXgKJbOw36KonYOKELtirFXNx3IMSyb+cpmU5dLBDbh1oEbDZNalYHTaTcXEuZFsfmNlVbn9ssPEfmidnbYcrQefyImOnjzm2NG85z7xP+qnjqlENWNrpH36P3fjWAd/pj39kh5ivTjZ+sSLXTx9Z6Mjsa4nO/4AKbBFcot1In/dX1T27HBMNK0uqK+GTyb7yypjCAbufc+8BgA9+PybU3OptrtBnDy9dJ1g63wve0sqM6XGIaK6S69RPnj0+OvLVyIocO36GEusjMSiInoihJg4sOnU9gELBXjQqOrrmpV8J5aZ5en6ymdJ83QHCEilqq/W0fIXJYoowoYimd2XmiuP1geP1ndfauTKHYUGKSOBCaFi8M2b1+unjzduXO/WTnG1YMcxgOab5bFGamc9c6TTbqj8TDecfuvWWKJ1+kZrrtBBIMMxvBDmbVR2jB/eNXlERyodEFUuN8ba6ZSeJvKWAvd2DB1KQUzMmplLa9OBkbIU9EBW4fGGPYdRuLHryOmRsYlDZy7KP525fDUOXqUeNaLGweMYXrm6a+++46fOHzxyYnIG4YO8g8ggx5BaiJNJ4R9AfB3NOlYhb5eL9UqxWS0iqUx0AzxLFm6MO8CCqDZUSTQk1rzPfgoys5B1dmRbMUc9gp7JFMS0XrDCwg0m2GmEVScwh9P00l0AYUZVoLCyqhlleIB8yzxjq8qU9uJCSrkVEtt7h5v/tLw2lW2rIwxdojjWf19UNZJJzMjXkIFBnuh/symJOSPj27OuEm4T5IVfT41NzQh6vvvKiuuTMw88+zoCyc8sX3/i4uiuo6fln0ZuTPZ0PKV4psplNqGOIQqHXMcQ7fqon9myc8/aTVtRPUvkF64TZxy5DEbiQhZhpGa4OmtUCyV37oDtzTw6a2dXjIxyKLBs+pZyISMRKA+wLBqgnTUKR6Ovi1PgxPFoPGgEFbtR9Smjh+5KJvcOsyKlv4Bi360WlQXbNcHHV5bVAKO7Flf3XW5MZtq7Ljb/9AlyEZ7dVXfCY6zhSCBxZE8BusEhDBzY4hF7/sRcz4ZPKyQQgAW91zXVEWVArQT0IPLTs6nMy2u2uqpwLI7EovJLHnQbUaOsHEM8RjSL2amzQj5juWU5qGwBmKplwKXYtmMNBkfmuGfmu23N9daDSA7a+Uoxo+wqE1hzXr6vnuNfe9bgxjQq9empNCwPM824Aq7JIY18ZV43sGRH7ZfdkJhqU4rdXGOlGqJNLH9JcZy63rCf9tzKTQKg+xe//s2nXlm+dc9Dz7/12KuD9y9+TSz60bHxOBEHRN5xd8Z3DK9cG9uyawj75dGrMIkLLvQDqvBi7PWgRj29JLpl56ODWE1rrlrMVsooMqsED2nCLV715XOiL0qvuu+40W2d9wJmEMQMMG1xh4moUaQdlx1poIQQKch/eKM2eKwhiUgZ0iddImKsBEaxVY+oCP6YDZ8OsEJ+47nR6wAQ3MBX1m3T9+8sWY7XUYoTM5QlqehcJLBoDJ3XYZ+amX1jcA2b9o4OBYle38CyC416LRDHsrI8ZtyKyEC3rWlIGKQAylr/iwksOBpBuSorojosGljV2FWzOhpkynKdKuAcidhqtwPrJnTuMh4W0pb21GBt4r5BH3UcZ0xBBNMwNGBgHAvFXplsLqazKcCKlljcSO3ce+CQ2L3/wLah3WAo1SlJ0eAQqvJIW02b/l051clerdtV/3w5mPC4UMBVOwJVOGwpUbNS0poQNVZuaO9+vwlJE8lD6JF6AquPOn/NlOH+zyq0oX6t6zxYnWart6nVS8oNjJsMNlqdK1LCOl6o5cEBbh91vRHNDii8efCFtwxUPfjcm+PovIudNHS7KqLafqSrJyzn0y02DwqLt/Pjrez1RmG6Zc22C5MY1NPO3WilR9qp4Wb6ilWgsYFS3c8M7Tnm48hQ5YWvkcsL1mzTwmzpHqgiYG0fGvL/NhlWuDBgxQ8H6xgq81heAZZxfLLYQq4vRRG5o0G3D7zapCDNFEbzcU8W3Yqv9EA4ZIkS3a5dunrzXi+wTl0c6St7LcXvQmbZ0zGUwSphb2tT61WhjawwYWiinb0K9LQyV2zemsXZdma0nRtr5ifymTmwbYCiH3UbVHtR9RAC0KizXCY6jQPTSsR570GYgQQVPClUmImKvAtdUU6GDNwRiSXhO6VxpD0GwCpaZtiQ+GpDKsS57aDMpFPZwLOSRiu/0pF2muhmYlnOMBbrrfuPKlSt2Lp7ATTP3K5TtUqlno6hnEmKa0cRdt+5Z29eE8CNcq6dHgWYOtlrLJym2rnr7fTlJoRWKd2muANpw2YlbVeiOL0bFatezt8mqnj4L1phmgNbt+9CDtz/k4j+wKGplTSn0Ipm7xSwhH1Vv2PEMSwFhQ1Vc3CY38dWix2BPEMQ3pGGz5fXbAGqEM1KpvobMCZkqmQs0sSrQiS1X1bdeGs2bcODw8dPgJhq2869XUAAPYBUJdOzgqpuhxbq9PAuq+mWlUD4rxY50rdFcqGEOgvUXwwEUulxgXaojRVNZl+NTevLk1fNiIPtOoaGRgt00yT6IhGHwCi2FpLwmE13pOHz5MWRhYkroX+SLv5MJhOVMXQdQ2ivpavXHzp+ctOOIUTeD0gnpgMsJ1DZC1h5hEYDv8XfbmoEQtuwqwr56GHjMIJRfIxaHew0YRUvQR4Y6cJoOykiXhy/mE7ok3Q0AMpkidPMtKLP5itVQqj0xO/jgs9gxzAwfxJ/fl3EhpzPWxt3Xr0x3i/xteRQIEzxm4itn7v+2SL2GbvaJUKMdOvQ3immPtQNmEbMiIONDuYAzogeAQtC1UwmlYiILAhZC5x8B1XcnzfADnzLiJjBwoggZI52kmMW0zlzjjQ0WGTW1HCK/uhOSSNa8UdfuEW2Fha5FeRxn0jpDjZ8dml5mB6s39JTNX1OCjjlavs9Br9jiNLQob0HDh05WtAiLJ1yOlbMvZKNw5nmKboqTWfTCeA4AlVwjqAZmO9vTvohsP1/mzVD0klEJlMAAAAASUVORK5CYII=", - "description": "TTrip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", + "description": "Trip animation on the Google maps. Allows to visualize location change over time. Use Trip Animation widget for advanced features.", "descriptor": { "type": "timeseries", "sizeX": 8.5, From 36c700810492ea3d39a24a50225f1c6ce6f0c1e4 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Fri, 4 Jun 2021 16:26:44 +0300 Subject: [PATCH 42/75] LWM2M: front add Firmware/Software update strategy --- .../widget_bundles/control_widgets.json | 6 +- .../AbstractLwm2mTransportResource.java | 4 +- .../DefaultLwM2MTransportMsgHandler.java | 14 ++++- .../server/LwM2mTransportCoapResource.java | 4 +- .../lwm2m/server/LwM2mTransportRequest.java | 10 ++-- .../lwm2m/server/client/LwM2mClient.java | 5 -- .../server/client/LwM2mClientContextImpl.java | 2 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 6 +- ...ile-transport-configuration.component.html | 58 ++++++++++++++----- ...ofile-transport-configuration.component.ts | 15 +++-- .../lwm2m/lwm2m-profile-config.models.ts | 8 ++- .../assets/locale/locale.constant-en_US.json | 12 +++- 12 files changed, 97 insertions(+), 47 deletions(-) diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index 8fea056506..cd0d95ef2a 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -17,9 +17,9 @@ "sizeY": 5.5, "resources": [], "templateHtml": "
", - "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n", - "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = utils.guid();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n \n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n \nself.onDestroy = function() {\n}", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\"\n ]\n}", + "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", + "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = utils.guid();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n \n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (styleObj && styleObj !== null) {\n terminal.css(styleObj);\n }\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n \nself.onDestroy = function() {\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java index 28c10df718..50eb468b17 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java @@ -24,11 +24,9 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; @Slf4j public abstract class AbstractLwm2mTransportResource extends LwM2mCoapResource { - protected final LwM2mTransportMsgHandler handler; - public AbstractLwm2mTransportResource(LwM2mTransportMsgHandler handler, String name) { + public AbstractLwm2mTransportResource(String name) { super(name); - this.handler = handler; } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 0fe5df844f..8dc1bd64c9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -61,6 +61,7 @@ import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; @@ -201,7 +202,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler .setSessionType(TransportProtos.SessionType.ASYNC).build()) .build(), null); this.getInfoFirmwareUpdate(lwM2MClient, null); -// this.getInfoSoftwareUpdate(lwM2MClient, null); + this.getInfoSoftwareUpdate(lwM2MClient, null); this.initLwM2mFromClientValue(registration, lwM2MClient); this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); } else { @@ -660,10 +661,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler /** version != null * set setClient_fw_info... = value **/ - if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); } - if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); } @@ -1376,11 +1377,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { log.warn("7) firmware start with ver: [{}]", response.getVersion()); + lwM2MClient.setFwUpdate(new LwM2mFwSwUpdate(lwM2MClient, OtaPackageType.FIRMWARE)); +// clientContext.getProfile(lwM2MClient.getProfileId()).getPostAttributeLwm2mProfile(); + lwM2MClient.getFwUpdate().setTypeUpdateByURL(true); lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); log.warn("11) OtaPackageIdMSB: [{}] OtaPackageIdLSB: [{}]", response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); lwM2MClient.getFwUpdate().setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); + if (rpcRequest == null) { lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); } else { @@ -1411,6 +1416,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.SOFTWARE.name())) { + lwM2MClient.setSwUpdate(new LwM2mFwSwUpdate(lwM2MClient, OtaPackageType.SOFTWARE)); +// clientContext.getProfile(lwM2MClient.getProfileId()).getPostAttributeLwm2mProfile(); + lwM2MClient.getSwUpdate().setTypeUpdateByURL(false); lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 0f2e885274..f2aa4dbf24 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -37,9 +37,11 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); + private final LwM2mTransportMsgHandler handler; public LwM2mTransportCoapResource(LwM2mTransportMsgHandler handler, String name) { - super(handler, name); + super(name); + this.handler = handler; this.setObservable(true); // enable observing this.addObserver(new CoapResourceObserver()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 4450859e3e..98b40e74fc 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -163,12 +163,12 @@ public class LwM2mTransportRequest { if (lwm2mClientRpcRequest != null) { ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; - this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } } else if (lwm2mClientRpcRequest != null) { String errorMsg = String.format("Path %s not found in object version", targetIdVer); - this.handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); + handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); } } else { switch (typeOper) { @@ -185,10 +185,10 @@ public class LwM2mTransportRequest { } String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, typeOper.name(), paths); - this.handler.sendLogsToThingsboard(msg, registration.getId()); + handler.sendLogsToThingsboard(msg, registration.getId()); if (lwm2mClientRpcRequest != null) { String valueMsg = String.format("Paths - %s", paths); - this.handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); + handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); } break; case OBSERVE_CANCEL: @@ -208,7 +208,7 @@ public class LwM2mTransportRequest { break; // lwm2mClientRpcRequest != null case FW_UPDATE: - this.handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); + handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); break; } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index a6a1fadbf3..602596ed2b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -31,7 +31,6 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; @@ -75,8 +74,6 @@ public class LwM2mClient implements Cloneable { @Getter private UUID deviceId; @Getter - private UUID sessionId; - @Getter private SessionInfoProto session; @Getter private UUID profileId; @@ -120,8 +117,6 @@ public class LwM2mClient implements Cloneable { this.init = false; this.queuedRequests = new ConcurrentLinkedQueue<>(); - this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE); - this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE); if (this.credentials != null && this.credentials.hasDeviceInfo()) { this.session = createSession(nodeId, sessionId, credentials); this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index a29edf6424..da59528fc1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -192,7 +192,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClientProfile getProfile(Registration registration) { - return this.getProfiles().get(getOrRegister(registration).getProfileId()); + return this.getProfiles().get(this.getOrRegister(registration).getProfileId()); } @Override diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index c3976c84ac..9cafc8f792 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -32,12 +32,12 @@ import java.util.List; import java.util.UUID; import java.util.concurrent.CopyOnWriteArrayList; +import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; @@ -107,12 +107,16 @@ public class LwM2mFwSwUpdate { @Getter @Setter private volatile Lwm2mClientRpcRequest rpcRequest; + @Getter + @Setter + private volatile boolean typeUpdateByURL; public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) { this.lwM2MClient = lwM2MClient; this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.type = type; this.stateUpdate = null; + this.typeUpdateByURL = false; this.initPathId(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index a29605a527..7c3ec6cacf 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -20,20 +20,6 @@
-
- - {{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }} - - {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: - {count: 2} }} - - -
- +
@@ -153,6 +139,48 @@
+ + +
+
+ + {{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }} + + {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: + {count: 2} }} + + +
+
+ + {{ 'device-profile.lwm2m.fw-update-url-label' | translate }} + + {{ 'device-profile.lwm2m.fw-update-url' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.fw-update-url' | translate: + {count: 2} }} + {{ 'device-profile.lwm2m.fw-update-url' | translate: + {count: 3} }} + + + + {{ 'device-profile.lwm2m.sw-update-url-label' | translate }} + + {{ 'device-profile.lwm2m.sw-update-url' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.sw-update-url' | translate: + {count: 2} }} + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 0a943ab5af..3342d24791 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -80,7 +80,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro constructor(private fb: FormBuilder, private deviceProfileService: DeviceProfileService) { this.lwm2mDeviceProfileFormGroup = this.fb.group({ - clientOnlyObserveAfterConnect: [1, []], objectIds: [null, Validators.required], observeAttrTelemetry: [null, Validators.required], shortId: [null, Validators.required], @@ -90,6 +89,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding:[], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], + clientOnlyObserveAfterConnect: [1, []], + fwUpdateUrl: [1, []], + swUpdateUrl: [1, []], }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] @@ -150,7 +152,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateWriteValue = (value: ModelValue): void => { this.lwm2mDeviceProfileFormGroup.patchValue({ - clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), shortId: this.configurationValue.bootstrap.servers.shortId, @@ -159,7 +160,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro notifIfDisabled: this.configurationValue.bootstrap.servers.notifIfDisabled, binding: this.configurationValue.bootstrap.servers.binding, bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, - lwm2mServer: this.configurationValue.bootstrap.lwm2mServer + lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, + clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, + fwUpdateUrl: this.configurationValue.clientLwM2mSettings.fwUpdateUrl, + swUpdateUrl: this.configurationValue.clientLwM2mSettings.swUpdateUrl }, {emitEvent: false}); } @@ -181,8 +185,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro private updateDeviceProfileValue(config): void { if (this.lwm2mDeviceProfileFormGroup.valid) { - this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = - config.clientOnlyObserveAfterConnect; this.updateObserveAttrTelemetryFromGroupToJson(config.observeAttrTelemetry.clientLwM2M); this.configurationValue.bootstrap.bootstrapServer = config.bootstrapServer; this.configurationValue.bootstrap.lwm2mServer = config.lwm2mServer; @@ -192,6 +194,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; bootstrapServers.notifIfDisabled = config.notifIfDisabled; bootstrapServers.binding = config.binding; + this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect; + this.configurationValue.clientLwM2mSettings.fwUpdateUrl = config.fwUpdateUrl; + this.configurationValue.clientLwM2mSettings.swUpdateUrl = config.swUpdateUrl; this.upDateJsonAllConfig(); this.updateModel(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index acea41394a..af4bdc79b0 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -166,7 +166,9 @@ export interface Lwm2mProfileConfigModels { } export interface ClientLwM2mSettings { - clientOnlyObserveAfterConnect: number; + clientOnlyObserveAfterConnect: string; + fwUpdateUrl: string; + swUpdateUrl: string; } export interface ObservableAttributes { @@ -236,7 +238,9 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientOnlyObserveAfterConnect: 1 + clientOnlyObserveAfterConnect: "1", + fwUpdateUrl: "1", + swUpdateUrl: "1" }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 08ddca091d..f23ff7c3ab 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1186,9 +1186,6 @@ "device-profile-file": "Device profile file", "invalid-device-profile-file-error": "Unable to import device profile: Invalid device profile data structure.", "lwm2m": { - "client-only-observe-after-connect-label": "Strategy", - "client-only-observe-after-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", - "client-only-observe-after-connect-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", "object-list": "Object list", "object-list-empty": "No objects selected.", "no-objects-matching": "No objects matching '{{object}}' were found.", @@ -1236,6 +1233,7 @@ "default-min-period": "Minimum Period between two notifications (sec)", "notif-if-disabled": "Notification Storing When Disabled or Offline", "binding": "Binding", + "bootstrap-tab": "Bootstrap", "bootstrap-server": "Bootstrap Server", "lwm2m-server": "LwM2M Server", "server-host": "Host", @@ -1248,6 +1246,14 @@ "client-hold-off-time-tip": "Client Hold Off Time for use with a Bootstrap-Server only", "bootstrap-server-account-timeout": "Account after the timeout", "bootstrap-server-account-timeout-tip": "Bootstrap-Server Account after the timeout value given by this resource.", + "others-tab": "Other settings...", + "client-only-observe-after-connect-label": "Strategy", + "client-only-observe-after-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", + "client-only-observe-after-connect-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", + "fw-update-url-label": "Firmware update strategy", + "fw-update-url": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", + "sw-update-url-label": "Software update strategy", + "sw-update-url": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", "config-json-tab": "Json Config Profile Device" } }, From 6fc2336f4606749d7fd46b737f03c050a52ea7c6 Mon Sep 17 00:00:00 2001 From: Chantsova Ekaterina Date: Fri, 4 Jun 2021 16:55:00 +0300 Subject: [PATCH 43/75] Entity data subscription: remove duplicate call of onData function --- ui-ngx/src/app/core/api/entity-data-subscription.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui-ngx/src/app/core/api/entity-data-subscription.ts b/ui-ngx/src/app/core/api/entity-data-subscription.ts index 9c769f3777..82a8351127 100644 --- a/ui-ngx/src/app/core/api/entity-data-subscription.ts +++ b/ui-ngx/src/app/core/api/entity-data-subscription.ts @@ -667,8 +667,7 @@ export class EntityDataSubscription { if (prevDataCb) { dataAggregator.updateOnDataCb(prevDataCb); } - } - if (!this.history && !isUpdate) { + } else if (!this.history && !isUpdate) { this.onData(subscriptionData, DataKeyType.timeseries, dataIndex, true, dataUpdatedCb); } } From 0b256a1d2c6b07972122ed468879069bfd5f8f4a Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Fri, 4 Jun 2021 19:08:01 +0300 Subject: [PATCH 44/75] Add hide dashboard toolbar settings. Improve dashboard setting dialog. Handle dashboard right layout toggle in mobile app. --- .../core/services/dashboard-utils.service.ts | 1 - .../src/app/core/services/mobile.service.ts | 25 ++- .../dashboard-page.component.ts | 39 +++- .../dashboard-settings-dialog.component.html | 194 +++++++++--------- .../dashboard-settings-dialog.component.scss | 12 +- .../dashboard-settings-dialog.component.ts | 96 +++++++-- .../layout/dashboard-layout.component.html | 1 - .../components/color-input.component.html | 2 +- .../src/app/shared/models/dashboard.models.ts | 2 +- .../app/shared/models/window-message.model.ts | 2 +- .../assets/locale/locale.constant-en_US.json | 7 +- 11 files changed, 254 insertions(+), 127 deletions(-) diff --git a/ui-ngx/src/app/core/services/dashboard-utils.service.ts b/ui-ngx/src/app/core/services/dashboard-utils.service.ts index f667fb5788..0dc143e6cb 100644 --- a/ui-ngx/src/app/core/services/dashboard-utils.service.ts +++ b/ui-ngx/src/app/core/services/dashboard-utils.service.ts @@ -231,7 +231,6 @@ export class DashboardUtilsService { private createDefaultGridSettings(): GridSettings { return { backgroundColor: '#eeeeee', - color: 'rgba(0,0,0,0.870588)', columns: 24, margin: 10, backgroundSizeMode: '100%' diff --git a/ui-ngx/src/app/core/services/mobile.service.ts b/ui-ngx/src/app/core/services/mobile.service.ts index 800651356a..cc940d6f85 100644 --- a/ui-ngx/src/app/core/services/mobile.service.ts +++ b/ui-ngx/src/app/core/services/mobile.service.ts @@ -27,6 +27,7 @@ import { AuthService } from '@core/auth/auth.service'; const dashboardStateNameHandler = 'tbMobileDashboardStateNameHandler'; const dashboardLoadedHandler = 'tbMobileDashboardLoadedHandler'; +const dashboardLayoutHandler = 'tbMobileDashboardLayoutHandler'; const navigationHandler = 'tbMobileNavigationHandler'; const mobileHandler = 'tbMobileHandler'; @@ -43,6 +44,7 @@ export class MobileService { private reloadUserObservable: Observable; private lastDashboardId: string; + private toggleLayoutFunction: () => void; constructor(@Inject(WINDOW) private window: Window, private router: Router, @@ -65,12 +67,26 @@ export class MobileService { } } - public onDashboardLoaded() { + public onDashboardLoaded(hasRightLayout: boolean, rightLayoutOpened: boolean) { if (this.mobileApp) { - this.mobileChannel.callHandler(dashboardLoadedHandler); + this.mobileChannel.callHandler(dashboardLoadedHandler, hasRightLayout, rightLayoutOpened); } } + public onDashboardRightLayoutChanged(opened: boolean) { + if (this.mobileApp) { + this.mobileChannel.callHandler(dashboardLayoutHandler, opened); + } + } + + public registerToggleLayoutFunction(toggleLayoutFunction: () => void) { + this.toggleLayoutFunction = toggleLayoutFunction; + } + + public unregisterToggleLayoutFunction() { + this.toggleLayoutFunction = null; + } + public handleWidgetMobileAction(type: WidgetMobileActionType, ...args: any[]): Observable> { if (this.mobileApp) { @@ -110,6 +126,11 @@ export class MobileService { const reloadUserMessage: ReloadUserMessage = message.data; this.reloadUser(reloadUserMessage); break; + case 'toggleDashboardLayout': + if (this.toggleLayoutFunction) { + this.toggleLayoutFunction(); + } + break; } } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts index 89629bfe49..cc19e98a99 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-page.component.ts @@ -149,8 +149,16 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC @Input() currentState: string; + private hideToolbarValue = false; + @Input() - hideToolbar: boolean; + set hideToolbar(hideToolbar: boolean) { + this.hideToolbarValue = hideToolbar; + } + + get hideToolbar(): boolean { + return (this.hideToolbarValue || this.hideToolbarSetting()) && !this.isEdit; + } @Input() syncStateWithQueryParam = true; @@ -269,6 +277,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC return !this.widgetEditMode && !this.hideToolbar && (this.toolbarAlwaysOpen() || this.isToolbarOpened || this.isEdit || this.showRightLayoutSwitch()); } + set toolbarOpened(toolbarOpened: boolean) { } @@ -329,7 +338,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.dashboardCtx.aliasController.updateAliases(); setTimeout(() => { this.mobileService.handleDashboardStateName(this.dashboardCtx.stateController.getCurrentStateName()); - this.mobileService.onDashboardLoaded(); + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened); }); } } @@ -340,6 +349,14 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.isMobile = !state.matches; } )); + if (this.isMobileApp) { + this.mobileService.registerToggleLayoutFunction(() => { + setTimeout(() => { + this.toggleLayouts(); + this.cd.detectChanges(); + }); + }); + } } private init(data: any) { @@ -430,6 +447,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } ngOnDestroy(): void { + if (this.isMobileApp) { + this.mobileService.unregisterToggleLayoutFunction(); + } this.rxSubscriptions.forEach((subscription) => { subscription.unsubscribe(); }); @@ -469,6 +489,15 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } } + private hideToolbarSetting(): boolean { + if (this.dashboard.configuration.settings && + isDefined(this.dashboard.configuration.settings.hideToolbar)) { + return this.dashboard.configuration.settings.hideToolbar; + } else { + return false; + } + } + public displayTitle(): boolean { if (this.dashboard.configuration.settings && isDefined(this.dashboard.configuration.settings.showTitle)) { @@ -546,15 +575,17 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC } public showRightLayoutSwitch(): boolean { - return this.isMobile && this.layouts.right.show; + return this.isMobile && !this.isMobileApp && this.layouts.right.show; } public toggleLayouts() { this.isRightLayoutOpened = !this.isRightLayoutOpened; + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); } public openRightLayout() { this.isRightLayoutOpened = true; + this.mobileService.onDashboardRightLayoutChanged(this.isRightLayoutOpened); } public mainLayoutWidth(): string { @@ -782,7 +813,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC this.updateLayouts(layoutsData); } setTimeout(() => { - this.mobileService.onDashboardLoaded(); + this.mobileService.onDashboardLoaded(this.layouts.right.show, this.isRightLayoutOpened); }); } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html index f84405faed..14c2df301b 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.html @@ -15,7 +15,7 @@ limitations under the License. --> - +

{{settings ? 'dashboard.settings' : 'layout.settings'}}

@@ -28,8 +28,8 @@
-
-
+
+
dashboard.state-controller @@ -38,112 +38,116 @@ -
- - {{ 'dashboard.toolbar-always-open' | translate }} - - +
+ dashboard.title-settings + {{ 'dashboard.display-title' | translate }} - + -
-
- +
+
+ dashboard.dashboard-logo-settings + + {{ 'dashboard.display-dashboard-logo' | translate }} + + + +
+
+ dashboard.toolbar-settings + + {{ 'dashboard.hide-toolbar' | translate }} + + + {{ 'dashboard.toolbar-always-open' | translate }} + + {{ 'dashboard.display-dashboards-selection' | translate }} - - + + {{ 'dashboard.display-entities-selection' | translate }} - - + + {{ 'dashboard.display-filters' | translate }} - - + + {{ 'dashboard.display-dashboard-timewindow' | translate }} - - + + {{ 'dashboard.display-dashboard-export' | translate }} - - + + {{ 'dashboard.display-update-dashboard-image' | translate }} - -
- - {{ 'dashboard.display-dashboard-logo' | translate }} - - - + +
-
- - - - dashboard.columns-count - - - {{ 'dashboard.columns-count-required' | translate }} - - - {{ 'dashboard.min-columns-count-message' | translate }} - - - {{ 'dashboard.max-columns-count-message' | translate }} - - - - dashboard.widgets-margins - - - {{ 'dashboard.margin-required' | translate }} - - - {{ 'dashboard.min-margin-message' | translate }} - - - {{ 'dashboard.max-margin-message' | translate }} - - - - {{ 'dashboard.autofill-height' | translate }} - - - - - - - dashboard.background-size-mode - - Fit width - Fit height - Cover - Contain - Original size - - - dashboard.mobile-layout -
- +
+
+ dashboard.layout-settings + + dashboard.columns-count + + + {{ 'dashboard.columns-count-required' | translate }} + + + {{ 'dashboard.min-columns-count-message' | translate }} + + + {{ 'dashboard.max-columns-count-message' | translate }} + + + + dashboard.widgets-margins + + + {{ 'dashboard.margin-required' | translate }} + + + {{ 'dashboard.min-margin-message' | translate }} + + + {{ 'dashboard.max-margin-message' | translate }} + + + + {{ 'dashboard.autofill-height' | translate }} + + + + + + + dashboard.background-size-mode + + Fit width + Fit height + Cover + Contain + Original size + + +
+
+ dashboard.mobile-layout + {{ 'dashboard.autofill-height' | translate }} - + dashboard.mobile-row-height -
+
diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss index f588cb8894..423c55ceea 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss @@ -14,13 +14,19 @@ * limitations under the License. */ :host { - small { - white-space: normal; + .fields-group { + padding: 0 8px 8px; + margin: 10px 0; + border: 1px groove rgba(0, 0, 0, .25); + border-radius: 4px; + legend { + color: rgba(0, 0, 0, .7); + } } } :host ::ng-deep { - .mat-checkbox-label { + .mat-slide-toggle-content { white-space: normal; } } diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts index 26ab9a3e3b..5d17d33eaa 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.ts @@ -71,19 +71,33 @@ export class DashboardSettingsDialogComponent extends DialogComponent { @@ -92,13 +106,52 @@ export class DashboardSettingsDialogComponent extends DialogComponent { + if (showTitleValue) { + this.settingsFormGroup.get('titleColor').enable(); + } else { + this.settingsFormGroup.get('titleColor').disable(); + } + } + ); + this.settingsFormGroup.get('showDashboardLogo').valueChanges.subscribe( + (showDashboardLogoValue: boolean) => { + if (showDashboardLogoValue) { + this.settingsFormGroup.get('dashboardLogoUrl').enable(); + } else { + this.settingsFormGroup.get('dashboardLogoUrl').disable(); + } + } + ); + this.settingsFormGroup.get('hideToolbar').valueChanges.subscribe( + (hideToolbarValue: boolean) => { + if (hideToolbarValue) { + this.settingsFormGroup.get('toolbarAlwaysOpen').disable(); + this.settingsFormGroup.get('showDashboardsSelect').disable(); + this.settingsFormGroup.get('showEntitiesSelect').disable(); + this.settingsFormGroup.get('showFilters').disable(); + this.settingsFormGroup.get('showDashboardTimewindow').disable(); + this.settingsFormGroup.get('showDashboardExport').disable(); + this.settingsFormGroup.get('showUpdateDashboardImage').disable(); + } else { + this.settingsFormGroup.get('toolbarAlwaysOpen').enable(); + this.settingsFormGroup.get('showDashboardsSelect').enable(); + this.settingsFormGroup.get('showEntitiesSelect').enable(); + this.settingsFormGroup.get('showFilters').enable(); + this.settingsFormGroup.get('showDashboardTimewindow').enable(); + this.settingsFormGroup.get('showDashboardExport').enable(); + this.settingsFormGroup.get('showUpdateDashboardImage').enable(); + } + } + ); } else { this.settingsFormGroup = this.fb.group({}); } if (this.gridSettings) { + const mobileAutoFillHeight = isUndefined(this.gridSettings.mobileAutoFillHeight) ? false : this.gridSettings.mobileAutoFillHeight; this.gridSettingsFormGroup = this.fb.group({ - color: [this.gridSettings.color || 'rgba(0,0,0,0.870588)', []], columns: [this.gridSettings.columns || 24, [Validators.required, Validators.min(10), Validators.max(1000)]], margin: [isDefined(this.gridSettings.margin) ? this.gridSettings.margin : 10, [Validators.required, Validators.min(0), Validators.max(50)]], @@ -106,10 +159,19 @@ export class DashboardSettingsDialogComponent extends DialogComponent { + if (mobileAutoFillHeightValue) { + this.gridSettingsFormGroup.get('mobileRowHeight').disable(); + } else { + this.gridSettingsFormGroup.get('mobileRowHeight').enable(); + } + } + ); } else { this.gridSettingsFormGroup = this.fb.group({}); } @@ -133,10 +195,10 @@ export class DashboardSettingsDialogComponent extends DialogComponent
diff --git a/ui-ngx/src/app/shared/components/color-input.component.html b/ui-ngx/src/app/shared/components/color-input.component.html index fbf8aea853..a0d226f589 100644 --- a/ui-ngx/src/app/shared/components/color-input.component.html +++ b/ui-ngx/src/app/shared/components/color-input.component.html @@ -20,7 +20,7 @@ {{icon}} {{label}} -
+
diff --git a/ui-ngx/src/app/shared/models/dashboard.models.ts b/ui-ngx/src/app/shared/models/dashboard.models.ts index a782fe8256..684705590e 100644 --- a/ui-ngx/src/app/shared/models/dashboard.models.ts +++ b/ui-ngx/src/app/shared/models/dashboard.models.ts @@ -45,7 +45,6 @@ export interface WidgetLayouts { export interface GridSettings { backgroundColor?: string; - color?: string; columns?: number; margin?: number; backgroundSizeMode?: string; @@ -93,6 +92,7 @@ export interface DashboardSettings { showDashboardExport?: boolean; showUpdateDashboardImage?: boolean; toolbarAlwaysOpen?: boolean; + hideToolbar?: boolean; titleColor?: string; } diff --git a/ui-ngx/src/app/shared/models/window-message.model.ts b/ui-ngx/src/app/shared/models/window-message.model.ts index 078a7aefec..2073197e27 100644 --- a/ui-ngx/src/app/shared/models/window-message.model.ts +++ b/ui-ngx/src/app/shared/models/window-message.model.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage'; +export type WindowMessageType = 'widgetException' | 'widgetEditModeInited' | 'widgetEditUpdated' | 'openDashboardMessage' | 'reloadUserMessage' | 'toggleDashboardLayout'; export interface WindowMessage { type: WindowMessageType; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 08ddca091d..cea00051ff 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -721,6 +721,7 @@ "maximum-upload-file-size": "Maximum upload file size: {{ size }}", "cannot-upload-file": "Cannot upload file", "settings": "Settings", + "layout-settings": "Layout settings", "columns-count": "Columns count", "columns-count-required": "Columns count is required.", "min-columns-count-message": "Only 10 minimum column count is allowed.", @@ -743,15 +744,19 @@ "mobile-row-height-required": "Mobile row height value is required.", "min-mobile-row-height-message": "Only 5 pixels is allowed as minimum mobile row height value.", "max-mobile-row-height-message": "Only 200 pixels is allowed as maximum mobile row height value.", + "title-settings": "Title settings", "display-title": "Display dashboard title", - "toolbar-always-open": "Keep toolbar opened", "title-color": "Title color", + "toolbar-settings": "Toolbar settings", + "hide-toolbar": "Hide toolbar", + "toolbar-always-open": "Keep toolbar opened", "display-dashboards-selection": "Display dashboards selection", "display-entities-selection": "Display entities selection", "display-filters": "Display filters", "display-dashboard-timewindow": "Display timewindow", "display-dashboard-export": "Display export", "display-update-dashboard-image": "Display update dashboard image", + "dashboard-logo-settings": "Dashboard logo settings", "display-dashboard-logo": "Display logo in dashboard fullscreen mode", "dashboard-logo-image": "Dashboard logo image", "import": "Import dashboard", From 195ed51afa9ba44d2fc93ca2a680303aa96a9efe Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 4 Jun 2021 20:49:01 +0300 Subject: [PATCH 45/75] UI: Add copy button checksum --- .../src/app/core/http/ota-package.service.ts | 2 +- .../entity/entities-table.component.html | 28 +++++- .../entity/entities-table.component.ts | 4 +- .../entity/entities-table-config.models.ts | 13 ++- .../ota-update-table-config.resolve.ts | 66 +++++++------ .../ota-update/ota-update.component.html | 8 +- .../pages/ota-update/ota-update.component.ts | 11 +++ .../button/copy-button.component.html | 29 ++++++ .../button/copy-button.component.scss | 30 ++++++ .../button/copy-button.component.ts | 95 +++++++++++++++++++ .../ota-package-autocomplete.component.ts | 2 +- ui-ngx/src/app/shared/shared.module.ts | 7 +- .../assets/locale/locale.constant-en_US.json | 1 + 13 files changed, 256 insertions(+), 40 deletions(-) create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.html create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.scss create mode 100644 ui-ngx/src/app/shared/components/button/copy-button.component.ts diff --git a/ui-ngx/src/app/core/http/ota-package.service.ts b/ui-ngx/src/app/core/http/ota-package.service.ts index 0042f67b24..7e7f09726e 100644 --- a/ui-ngx/src/app/core/http/ota-package.service.ts +++ b/ui-ngx/src/app/core/http/ota-package.service.ts @@ -39,7 +39,7 @@ export class OtaPackageService { } public getOtaPackagesInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: OtaUpdateType, - hasData = true, config?: RequestConfig): Observable> { + config?: RequestConfig): Observable> { const url = `/api/otaPackages/${deviceProfileId}/${type}${pageLink.toQuery()}`; return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html index f4ecd52437..83f221f9e1 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.html @@ -163,8 +163,34 @@ *matCellDef="let entity; let row = index" [matTooltip]="cellTooltip(entity, column, row)" matTooltipPosition="above" - [innerHTML]="cellContent(entity, column, row)" [ngStyle]="cellStyle(entity, column, row)"> + + + + + + + + + + + + diff --git a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts index 40a6b0cece..df164adda4 100644 --- a/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/entity/entities-table.component.ts @@ -43,7 +43,7 @@ import { TranslateService } from '@ngx-translate/core'; import { BaseData, HasId } from '@shared/models/base-data'; import { ActivatedRoute } from '@angular/router'; import { - CellActionDescriptor, + CellActionDescriptor, CellActionDescriptorType, EntityActionTableColumn, EntityColumn, EntityTableColumn, @@ -104,6 +104,8 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn timewindow: Timewindow; dataSource: EntitiesDataSource>; + cellActionType = CellActionDescriptorType; + isDetailsOpen = false; detailsPanelOpened = new EventEmitter(); diff --git a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts index 4b057d8fef..8fcd2aa070 100644 --- a/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts +++ b/ui-ngx/src/app/modules/home/models/entity/entities-table-config.models.ts @@ -48,6 +48,9 @@ export type CellContentFunction> = (entity: T, key: st export type CellTooltipFunction> = (entity: T, key: string) => string | undefined; export type HeaderCellStyleFunction> = (key: string) => object; export type CellStyleFunction> = (entity: T, key: string) => object; +export type CopyCellContent> = (entity: T, key: string, length: number) => object; + +export enum CellActionDescriptorType { 'DEFAULT', 'COPY_BUTTON'} export interface CellActionDescriptor> { name: string; @@ -56,7 +59,8 @@ export interface CellActionDescriptor> { mdiIcon?: string; style?: any; isEnabled: (entity: T) => boolean; - onAction: ($event: MouseEvent, entity: T) => void; + onAction: ($event: MouseEvent, entity: T) => any; + type?: CellActionDescriptorType; } export interface GroupActionDescriptor> { @@ -95,7 +99,8 @@ export class EntityTableColumn> extends BaseEntityTabl public sortable: boolean = true, public headerCellStyleFunction: HeaderCellStyleFunction = () => ({}), public cellTooltipFunction: CellTooltipFunction = () => undefined, - public isNumberColumn: boolean = false) { + public isNumberColumn: boolean = false, + public actionCell: CellActionDescriptor = {isEnabled: () => false, name: null, onAction: () => ({})}) { super('content', key, title, width, sortable); } } @@ -187,3 +192,7 @@ export class EntityTableConfig, P extends PageLink = P export function checkBoxCell(value: boolean): string { return `${value ? 'check_box' : 'check_box_outline_blank'}`; } + +export function copyCellContent(value: string): string { + return `${value ? 'check_box' : 'check_box_outline_blank'}`; +} diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index d56b4e4541..dc7c36371d 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -17,6 +17,7 @@ import { Injectable } from '@angular/core'; import { Resolve } from '@angular/router'; import { + CellActionDescriptorType, DateEntityTableColumn, EntityTableColumn, EntityTableConfig @@ -61,27 +62,46 @@ export class OtaUpdateTableConfigResolve implements Resolve('createdTime', 'common.created-time', this.datePipe, '150px'), - new EntityTableColumn('title', 'ota-update.title', '25%'), - new EntityTableColumn('version', 'ota-update.version', '25%'), - new EntityTableColumn('type', 'ota-update.package-type', '25%', entity => { + new EntityTableColumn('title', 'ota-update.title', '20%'), + new EntityTableColumn('version', 'ota-update.version', '20%'), + new EntityTableColumn('type', 'ota-update.package-type', '20%', entity => { return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); }), - new EntityTableColumn('fileName', 'ota-update.file-name', '25%'), + new EntityTableColumn('url', 'ota-update.url', '20%', entity => { + return entity.url && entity.url.length > 20 ? `${entity.url.slice(0, 20)}…` : ''; + }, () => ({}), true, () => ({}), () => undefined, false, + { + name: this.translate.instant('ota-update.copy-checksum'), + icon: 'content_paste', + style: { + 'font-size': '16px', + color: 'rgba(0,0,0,.87)' + }, + isEnabled: (otaPackage) => !!otaPackage.url, + onAction: ($event, entity) => entity.url, + type: CellActionDescriptorType.COPY_BUTTON + }), + new EntityTableColumn('fileName', 'ota-update.file-name', '20%'), new EntityTableColumn('dataSize', 'ota-update.file-size', '70px', entity => { return entity.dataSize ? this.fileSize.transform(entity.dataSize) : ''; }), - new EntityTableColumn('checksum', 'ota-update.checksum', '540px', entity => { - return entity.checksum ? `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}` : ''; - }, () => ({}), false) - ); - - this.config.cellActionDescriptors.push( + new EntityTableColumn('checksum', 'ota-update.checksum', '220px', entity => { + return entity.checksum ? this.checksumText(entity) : ''; + }, () => ({}), true, () => ({}), () => undefined, false, { name: this.translate.instant('ota-update.copy-checksum'), - icon: 'content_copy', + icon: 'content_paste', + style: { + 'font-size': '16px', + color: 'rgba(0,0,0,.87)' + }, isEnabled: (otaPackage) => !!otaPackage.checksum, - onAction: ($event, entity) => this.copyPackageChecksum($event, entity) - }, + onAction: ($event, entity) => entity.checksum, + type: CellActionDescriptorType.COPY_BUTTON + }) + ); + + this.config.cellActionDescriptors.push( { name: this.translate.instant('ota-update.download'), icon: 'file_download', @@ -120,19 +140,12 @@ export class OtaUpdateTableConfigResolve implements Resolve 20) { + text = `${text.slice(0, 20)}…`; } - this.clipboardService.copy(otaPackageInfo?.checksum); - this.store.dispatch(new ActionNotificationShow( - { - message: this.translate.instant('ota-update.checksum-copied-message'), - type: 'success', - duration: 750, - verticalPosition: 'bottom', - horizontalPosition: 'right' - })); + return text; } onPackageAction(action: EntityAction): boolean { @@ -140,9 +153,6 @@ export class OtaUpdateTableConfigResolve implements Resolve diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index 42e0ffc33c..9193b37c8e 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -149,6 +149,17 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } + onPackageChecksumCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('ota-update.checksum-copied-message'), + type: 'success', + duration: 750, + verticalPosition: 'bottom', + horizontalPosition: 'right' + })); + } + prepareFormValue(formValue: any): any { delete formValue.resource; delete formValue.generateChecksum; diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html new file mode 100644 index 0000000000..55f3008cf7 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -0,0 +1,29 @@ + + diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.scss b/ui-ngx/src/app/shared/components/button/copy-button.component.scss new file mode 100644 index 0000000000..3cf3757c03 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.scss @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2021 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. + */ +:host { + .mat-icon-button{ + height: 32px; + width: 32px; + line-height: 32px; + .mat-icon.copied{ + color: #00C851!important; + } + } + &:hover{ + .mat-icon{ + color: #28567E !important; + } + } +} diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts new file mode 100644 index 0000000000..e8e4d8e4b8 --- /dev/null +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -0,0 +1,95 @@ +/// +/// Copyright © 2016-2021 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. +/// + +import { ChangeDetectorRef, Component, Input, ViewChild } from '@angular/core'; +import { ClipboardService } from 'ngx-clipboard'; +import { MatTooltip, TooltipPosition } from '@angular/material/tooltip/tooltip'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-copy-button', + styleUrls: ['copy-button.component.scss'], + templateUrl: './copy-button.component.html' +}) +export class CopyButtonComponent { + + private copedIcon = ''; + private timer; + + copied = false; + + @Input() + copyText: string; + + @Input() + disabled = false; + + @Input() + mdiIcon: string; + + @Input() + icon: string; + + @Input() + tooltipText: string; + + @Input() + tooltipPosition: TooltipPosition; + + @Input() + style: {[key: string]: any} = {}; + + constructor(private clipboardService: ClipboardService, + private translate: TranslateService, + private cd: ChangeDetectorRef) { + } + + @ViewChild('tooltip', {static: true}) tooltip: MatTooltip; + + copy($event: Event): void { + $event.stopPropagation(); + $event.preventDefault(); + if (this.timer) { + clearTimeout(this.timer); + } + this.clipboardService.copy(this.copyText); + this.copedIcon = 'done'; + this.copied = true; + this.tooltip.show(); + this.timer = setTimeout(() => { + this.copedIcon = null; + this.copied = false; + this.tooltip.hide(); + this.cd.detectChanges(); + }, 1500); + } + + get iconSymbol(): string { + return this.copedIcon || this.icon; + } + + get mdiIconSymbol(): string { + return this.copedIcon ? '' : this.mdiIcon; + } + + get matTooltipText(): string { + return this.copied ? this.translate.instant('ota-update.copied') : this.tooltipText; + } + + get matTooltipPosition(): TooltipPosition { + return this.copied ? 'below' : this.tooltipPosition; + } +} diff --git a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts index 25df909a8c..bb3be21fa8 100644 --- a/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/ota-package/ota-package-autocomplete.component.ts @@ -216,7 +216,7 @@ export class OtaPackageAutocompleteComponent implements ControlValueAccessor, On direction: Direction.ASC }); return this.otaPackageService.getOtaPackagesInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, - true, {ignoreLoading: true}).pipe( + {ignoreLoading: true}).pipe( map((data) => data && data.data.length ? data.data : null) ); } diff --git a/ui-ngx/src/app/shared/shared.module.ts b/ui-ngx/src/app/shared/shared.module.ts index 5e71d4c44a..f2bcdc97a4 100644 --- a/ui-ngx/src/app/shared/shared.module.ts +++ b/ui-ngx/src/app/shared/shared.module.ts @@ -143,6 +143,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; import { OtaPackageAutocompleteComponent } from '@shared/components/ota-package/ota-package-autocomplete.component'; import { MAT_DATE_LOCALE } from '@angular/material/core'; +import { CopyButtonComponent } from '@shared/components/button/copy-button.component'; @NgModule({ providers: [ @@ -240,7 +241,8 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; EntityGatewaySelectComponent, ContactComponent, OtaPackageAutocompleteComponent, - WidgetsBundleSearchComponent + WidgetsBundleSearchComponent, + CopyButtonComponent ], imports: [ CommonModule, @@ -412,7 +414,8 @@ import { MAT_DATE_LOCALE } from '@angular/material/core'; EntityGatewaySelectComponent, ContactComponent, OtaPackageAutocompleteComponent, - WidgetsBundleSearchComponent + WidgetsBundleSearchComponent, + CopyButtonComponent ] }) export class SharedModule { } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6f2618dd7a..da35b00d3a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -2166,6 +2166,7 @@ "content-type": "Content type", "copy-checksum": "Copy checksum", "copyId": "Copy package Id", + "copied": "Copied!", "delete": "Delete package", "delete-ota-update-text": "Be careful, after the confirmation the OTA update will become unrecoverable.", "delete-ota-update-title": "Are you sure you want to delete the OTA update '{{title}}'?", From cf899b6ffd49a8736213ea021ef0bbe80f34ab4e Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Sun, 6 Jun 2021 10:30:05 +0300 Subject: [PATCH 46/75] UI: Improvement tb0copy-button component --- .../components/button/copy-button.component.html | 4 ++-- .../components/button/copy-button.component.scss | 2 +- .../components/button/copy-button.component.ts | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html index 55f3008cf7..6b38870d3e 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.html +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -20,8 +20,8 @@ [disabled]="disabled" [matTooltip]="matTooltipText" [matTooltipPosition]="matTooltipPosition" - (mouseenter)="copied ? $event.stopImmediatePropagation():''" - (mouseleave)="copied ? $event.stopImmediatePropagation():''" + (mouseenter)="immediatePropagation($event)" + (mouseleave)="immediatePropagation($event)" (click)="copy($event)"> {{ iconSymbol }} diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.scss b/ui-ngx/src/app/shared/components/button/copy-button.component.scss index 3cf3757c03..0e5f5de8d9 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.scss +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.scss @@ -19,7 +19,7 @@ width: 32px; line-height: 32px; .mat-icon.copied{ - color: #00C851!important; + color: #00C851 !important; } } &:hover{ diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.ts b/ui-ngx/src/app/shared/components/button/copy-button.component.ts index e8e4d8e4b8..9e73ad9734 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.ts +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.ts @@ -14,9 +14,9 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Input, ViewChild } from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { ClipboardService } from 'ngx-clipboard'; -import { MatTooltip, TooltipPosition } from '@angular/material/tooltip/tooltip'; +import { MatTooltip, TooltipPosition } from '@angular/material/tooltip'; import { TranslateService } from '@ngx-translate/core'; @Component({ @@ -52,6 +52,9 @@ export class CopyButtonComponent { @Input() style: {[key: string]: any} = {}; + @Output() + successCopied: EventEmitter; + constructor(private clipboardService: ClipboardService, private translate: TranslateService, private cd: ChangeDetectorRef) { @@ -61,11 +64,11 @@ export class CopyButtonComponent { copy($event: Event): void { $event.stopPropagation(); - $event.preventDefault(); if (this.timer) { clearTimeout(this.timer); } this.clipboardService.copy(this.copyText); + this.successCopied.emit(this.copyText); this.copedIcon = 'done'; this.copied = true; this.tooltip.show(); @@ -92,4 +95,8 @@ export class CopyButtonComponent { get matTooltipPosition(): TooltipPosition { return this.copied ? 'below' : this.tooltipPosition; } + + immediatePropagation($event: Event): void { + this.copied ? $event.stopImmediatePropagation(): ''; + } } From 54ca0385c273073921c674dd91758eaa46d76280 Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Sun, 6 Jun 2021 14:01:08 +0300 Subject: [PATCH 47/75] UI: Fixed show new dashboard settings in Safari --- .../dashboard-page/dashboard-settings-dialog.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss index 423c55ceea..5587d9c35a 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/dashboard-settings-dialog.component.scss @@ -21,6 +21,7 @@ border-radius: 4px; legend { color: rgba(0, 0, 0, .7); + width: fit-content; } } } From 6fbd34d6a11d9227932e44fca4e15e7020ee9ec7 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 6 Jun 2021 21:46:30 +0300 Subject: [PATCH 48/75] LWM2M: firmwareUpdate & coapRecourse --- .../widget_bundles/control_widgets.json | 2 +- .../LwM2MServerSecurityInfoRepository.java | 6 +- .../src/main/resources/thingsboard.yml | 12 +- .../LwM2MTransportBootstrapService.java | 2 +- .../lwm2m/config/LwM2MSecureServerConfig.java | 4 + .../config/LwM2MTransportBootstrapConfig.java | 7 + .../config/LwM2MTransportServerConfig.java | 16 ++ ...va => AbstractLwM2mTransportResource.java} | 4 +- .../DefaultLwM2MTransportMsgHandler.java | 216 ++++++++--------- .../server/DefaultLwM2mTransportService.java | 11 +- .../lwm2m/server/LwM2mNetworkConfig.java | 17 +- .../lwm2m/server/LwM2mOtaConvert.java | 25 ++ .../server/LwM2mTransportCoapResource.java | 75 ++---- .../server/LwM2mTransportMsgHandler.java | 4 +- .../lwm2m/server/LwM2mTransportRequest.java | 42 ++-- .../lwm2m/server/LwM2mTransportUtil.java | 228 +++++++++++++++--- .../lwm2m/server/client/LwM2mClient.java | 17 ++ .../server/client/LwM2mClientProfile.java | 30 ++- ...equest.java => LwM2mClientRpcRequest.java} | 6 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 193 +++++++++++---- .../src/main/resources/tb-lwm2m-transport.yml | 20 +- ...ile-transport-configuration.component.html | 30 +-- ...ofile-transport-configuration.component.ts | 18 +- .../lwm2m/lwm2m-profile-config.models.ts | 12 +- .../assets/locale/locale.constant-en_US.json | 14 +- 25 files changed, 676 insertions(+), 335 deletions(-) rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/{AbstractLwm2mTransportResource.java => AbstractLwM2mTransportResource.java} (95%) create mode 100644 common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java rename common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/{Lwm2mClientRpcRequest.java => LwM2mClientRpcRequest.java} (98%) diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index cd0d95ef2a..23b0b90ac3 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -18,7 +18,7 @@ "resources": [], "templateHtml": "
", "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", - "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = utils.guid();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n \n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (styleObj && styleObj !== null) {\n terminal.css(styleObj);\n }\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n \nself.onDestroy = function() {\n}", + "controllerScript": "var requestTimeout = 500;\nvar multiParams = false;\nvar useRowStyleFunction = false;\nvar styleObj = {};\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var utils = self.ctx.$scope.$injector.get(self.ctx.servicesMap.get('utils'));\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.multiParams) {\n multiParams = self.ctx.settings.multiParams;\n }\n if (self.ctx.settings.useRowStyleFunction && self.ctx.settings.rowStyleFunction) {\n try {\n var style = self.ctx.settings.rowStyleFunction;\n styleObj = JSON.parse(style);\n if ((typeof styleObj !== \"object\")) {\n styleObj = null;\n throw new URIError(`${style === null ? 'null' : typeof style} instead of style object`);\n }\n else if (typeof styleObj === \"object\" && (typeof styleObj.length) === \"number\") {\n styleObj = null;\n throw new URIError('Array instead of style object');\n }\n }\n catch (e) {\n console.log(`Row style function in widget ` +\n `returns '${e}'. Please check your row style function.`); \n }\n useRowStyleFunction = self.ctx.settings.useRowStyleFunction;\n \n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var cmdObj = $.terminal.parse_command(localCommand);\n if (cmdObj.args) {\n if (!multiParams && cmdObj.args.length > 1) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n }\n else {\n if (cmdObj.args.length) {\n var params = getMultiParams(cmdObj.args);\n }\n performRpc(this, cmdObj.name, params, requestUUID);\n }\n }\n \n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (styleObj && styleObj !== null) {\n terminal.css(styleObj);\n }\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1 (multiParams===false):]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2 (multiParams===false):]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\":2,\\\\\"key2\\\\\":\\\\\"myVal\\\\\"}\"\\n\\n'; \n commandsListText += '[[b;#fff;]Example 3 (multiParams===true)]\\n'; \n commandsListText += ' [params body] = \"all the string after the method, including spaces\"]\\n';\n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": \"battery level\", \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\nfunction getMultiParams(cmdObj) {\n var params = \"\";\n cmdObj.forEach((element) => {\n try {\n params += \" \" + JSON.strigify(JSON.parse(element));\n } catch (e) {\n params += \" \" + element;\n }\n })\n return params.trim();\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n}", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n },\n \"multiParams\": {\n \"title\": \"RPC params All line\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useRowStyleFunction\": {\n \"title\": \"Use row style function\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"rowStyleFunction\": {\n \"title\": \"Row style function: f(entity, ctx)\",\n \"type\": \"string\",\n \"default\": \"\"\n }\n },\n \"required\": [\"requestTimeout\"]\n },\n \"form\": [\n \"requestTimeout\",\n \"multiParams\",\n \"useRowStyleFunction\",\n {\n \"key\": \"rowStyleFunction\",\n \"type\": \"javascript\",\n \"condition\": \"model.useRowStyleFunction === true\"\n }\n ]\n}", "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC debug terminal\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" diff --git a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java index 06190cdf70..e3b09ef044 100644 --- a/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/lwm2m/LwM2MServerSecurityInfoRepository.java @@ -66,18 +66,18 @@ public class LwM2MServerSecurityInfoRepository { bsServ.setServerId(serverConfig.getId()); switch (securityMode) { case NO_SEC: - bsServ.setHost(serverConfig.getHost()); + bsServ.setHost(serverConfig.getHostRequests()); bsServ.setPort(serverConfig.getPort()); bsServ.setServerPublicKey(""); break; case PSK: - bsServ.setHost(serverConfig.getSecureHost()); + bsServ.setHost(serverConfig.getSecureHostRequests()); bsServ.setPort(serverConfig.getSecurePort()); bsServ.setServerPublicKey(""); break; case RPK: case X509: - bsServ.setHost(serverConfig.getSecureHost()); + bsServ.setHost(serverConfig.getSecureHostRequests()); bsServ.setPort(serverConfig.getSecurePort()); bsServ.setServerPublicKey(getPublicKey(serverConfig.getCertificateAlias(), this.serverConfig.getPublicX(), this.serverConfig.getPublicY())); break; diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 78658fe8fd..7925f97e89 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -639,9 +639,13 @@ transport: server: id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" + # the host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_REQUESTS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT:5685}" security: bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" + # the security host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_SECURITY_REQUESTS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}" @@ -654,9 +658,13 @@ transport: enable: "${LWM2M_ENABLED_BS:true}" id: "${LWM2M_SERVER_ID_BS:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + # the host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_REQUESTS_BS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_BS:5687}" security: - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}" + # the security host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_SECURITY_REQUESTS_BS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" @@ -675,6 +683,8 @@ transport: root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}" timeout: "${LWM2M_TIMEOUT:120000}" + blockwise_lifetime: "${BLOCKWISE_LIFETIME:300000}" + block2_option_enable: "${BLOCK2_OPTION_ENABLED:true}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java index 9348cb31a5..04c2fd62fb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java @@ -102,7 +102,7 @@ public class LwM2MTransportBootstrapService { builder.setLocalSecureAddress(bootstrapConfig.getSecureHost(), bootstrapConfig.getSecurePort()); /** Create CoAP Config */ - builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort())); + builder.setCoapConfig(getCoapConfig(bootstrapConfig.getPort(), bootstrapConfig.getSecurePort(), serverConfig)); /** Define model provider (Create Models )*/ diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java index fcca9fb975..e30cc01456 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MSecureServerConfig.java @@ -21,10 +21,14 @@ public interface LwM2MSecureServerConfig { String getHost(); + String getHostRequests(); + Integer getPort(); String getSecureHost(); + String getSecureHostRequests(); + Integer getSecurePort(); String getPublicX(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java index c0379f9cee..45b6aa796b 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportBootstrapConfig.java @@ -34,6 +34,10 @@ public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.bootstrap.bind_address:}") private String host; + @Getter + @Value("${transport.lwm2m.bootstrap.bind_address_requests:}") + private String hostRequests; + @Getter @Value("${transport.lwm2m.bootstrap.bind_port:}") private Integer port; @@ -42,6 +46,9 @@ public class LwM2MTransportBootstrapConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.bootstrap.security.bind_address:}") private String secureHost; + @Getter + @Value("${transport.lwm2m.bootstrap.security.bind_address_requests:}") + private String secureHostRequests; @Getter @Value("${transport.lwm2m.bootstrap.security.bind_port:}") private Integer securePort; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java index 25c7766895..83601f4047 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/config/LwM2MTransportServerConfig.java @@ -44,6 +44,14 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.timeout:}") private Long timeout; + @Getter + @Value("${transport.lwm2m.blockwise_lifetime:}") + private Long blockwiseLifetime; + + @Getter + @Value("${transport.lwm2m.block2_option_enable:}") + private boolean block2OptionEnable; + @Getter @Value("${transport.sessions.report_timeout}") private long sessionReportTimeout; @@ -112,6 +120,10 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.server.bind_address:}") private String host; + @Getter + @Value("${transport.lwm2m.server.bind_address_requests:}") + private String hostRequests; + @Getter @Value("${transport.lwm2m.server.bind_port:}") private Integer port; @@ -120,6 +132,10 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { @Value("${transport.lwm2m.server.security.bind_address:}") private String secureHost; + @Getter + @Value("${transport.lwm2m.server.security.bind_address_requests:}") + private String secureHostRequests; + @Getter @Value("${transport.lwm2m.server.security.bind_port:}") private Integer securePort; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java similarity index 95% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java index 50eb468b17..2c82facd23 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwm2mTransportResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/AbstractLwM2mTransportResource.java @@ -23,9 +23,9 @@ import org.eclipse.leshan.core.californium.LwM2mCoapResource; import org.thingsboard.server.common.transport.TransportServiceCallback; @Slf4j -public abstract class AbstractLwm2mTransportResource extends LwM2mCoapResource { +public abstract class AbstractLwM2mTransportResource extends LwM2mCoapResource { - public AbstractLwm2mTransportResource(String name) { + public AbstractLwM2mTransportResource(String name) { super(name); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 8dc1bd64c9..d82dbcbf08 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -61,8 +61,8 @@ import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; -import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; @@ -89,18 +89,17 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.CLIENT_NOT_AUTHORIZED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_STRATEGY_2; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; @@ -110,8 +109,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; @@ -186,7 +185,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onRegistered(Registration registration, Collection previousObservations) { registrationExecutor.submit(() -> { try { - log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); + String msgReg = previousObservations != null ? String.format("%s %s Client: create after Registration", registration.getEndpoint(), registration.getId()) : + String.format("%s %s Client: create after UpdateRegistration", registration.getEndpoint(), registration.getId()); + log.warn(msgReg); LwM2mClient lwM2MClient = this.clientContext.registerOrUpdate(registration); if (lwM2MClient != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); @@ -204,7 +205,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler this.getInfoFirmwareUpdate(lwM2MClient, null); this.getInfoSoftwareUpdate(lwM2MClient, null); this.initLwM2mFromClientValue(registration, lwM2MClient); - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": " + msgReg, registration.getId()); } else { log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); } @@ -223,28 +224,33 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param registration - Registration LwM2M Client */ public void updatedReg(Registration registration) { - updateRegistrationExecutor.submit(() -> { - try { - LwM2mClient client = clientContext.getOrRegister(registration); - if (client != null && client.getSession() != null) { - SessionInfoProto sessionInfo = client.getSession(); - this.reportActivityAndRegister(sessionInfo); - if (registration.getBindingMode().useQueueMode()) { - LwM2mQueuedRequest request; - while ((request = client.getQueuedRequests().poll()) != null) { - request.send(); + try { + LwM2mClient client = clientContext.getOrRegister(registration); + if (client != null) { + updateRegistrationExecutor.submit(() -> { + if (client != null && client.getSession() != null) { + SessionInfoProto sessionInfo = client.getSession(); + this.reportActivityAndRegister(sessionInfo); + if (registration.getBindingMode().useQueueMode()) { + LwM2mQueuedRequest request; + while ((request = client.getQueuedRequests().poll()) != null) { + request.send(); + } } + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId()); + } else { + log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); + this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId()); } - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client update Registration", registration.getId()); - } else { - log.error("Client: [{}] updatedReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); - this.sendLogsToThingsboard(LOG_LW2M_ERROR + ": Client update Registration", registration.getId()); - } - } catch (Throwable t) { - log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); - this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId()); + }); } - }); + else { + this.onRegistered(registration, null); + } + } catch (Throwable t) { + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); + this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage()), registration.getId()); + } } /** @@ -304,7 +310,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param response - observe */ @Override - public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest) { if (response.getContent() != null) { LwM2mClient lwM2MClient = clientContext.getOrRegister(registration); ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); @@ -327,7 +333,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response, - Lwm2mClientRpcRequest rpcRequest) { + LwM2mClientRpcRequest rpcRequest) { Object value = null; if (response.getContent() instanceof LwM2mObject) { value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer); @@ -457,10 +463,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); if (!this.rpcSubscriptions.containsKey(requestUUID)) { this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); - Lwm2mClientRpcRequest lwm2mClientRpcRequest = null; + LwM2mClientRpcRequest lwm2mClientRpcRequest = null; try { Registration registration = clientContext.getClient(sessionInfo).getRegistration(); - lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); + lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); if (lwm2mClientRpcRequest.getErrorMsg() != null) { lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); @@ -472,7 +478,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } catch (Exception e) { if (lwm2mClientRpcRequest == null) { - lwm2mClientRpcRequest = new Lwm2mClientRpcRequest(); + lwm2mClientRpcRequest = new LwM2mClientRpcRequest(); } lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); if (lwm2mClientRpcRequest.getErrorMsg() == null) { @@ -494,7 +500,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); } - public void sentRpcResponse(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { + public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { rpcRequest.setResponseCode(requestCode); if (LOG_LW2M_ERROR.equals(typeMsg)) { rpcRequest.setInfoMsg(null); @@ -604,7 +610,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration); Set clientObjects = clientContext.getSupportedIdVerInClient(registration); if (clientObjects != null && clientObjects.size() > 0) { - if (LWM2M_STRATEGY_2 == LwM2mTransportUtil.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { + if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == lwM2MClientProfile.getClientStrategy()) { // #2 lwM2MClient.getPendingReadRequests().addAll(clientObjects); clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), @@ -668,53 +674,11 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); } - /** - * Before operation Execute (FwUpdate) inspection Update Result : - * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED - * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value - * - start Execute (FwUpdate) - * After finished operation Execute (FwUpdate) inspection Update Result : - * - after start operation Execute (FwUpdate): fw_state = UPDATING - * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") - * - finished operation Execute (FwUpdate) - */ - if (lwM2MClient.getFwUpdate() != null - && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { - if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate()) - && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) { - lwM2MClient.getFwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest); - } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate()) - && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) { - lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true); - } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate()) - && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterError()) { - lwM2MClient.getFwUpdate().finishFwSwUpdate(this, false); - } - } - - /** - * Before operation Execute (SwUpdate) inspection Update Result : - * - after finished operation Write result: success (SwUpdate): fw_state = DOWNLOADED - * - before operation Execute (SwUpdate) Update Result = 3 - Successfully Downloaded and package integrity verified - * - start Execute (SwUpdate) - * After finished operation Execute (SwUpdate) inspection Update Result : - * - after start operation Execute (SwUpdate): fw_state = UPDATING - * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed."" - * - after success finished operation Execute (SwUpdate) Update Result == 2 "Software successfully installed."" - * - finished operation Execute (SwUpdate) - */ - if (lwM2MClient.getSwUpdate() != null - && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) { - if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate()) - && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) { - lwM2MClient.getSwUpdate().executeFwSwWare(this, this.lwM2mTransportRequest); - } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate()) - && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) { - lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true); - } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate()) - && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterError()) { - lwM2MClient.getSwUpdate().finishFwSwUpdate(this, false); - } + if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || + (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { + LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); + log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); + fwUpdate.updateStateOta(this, lwM2mTransportRequest, registration, path, ((Long) lwM2mResource.getValue()).intValue()); } Set paths = new HashSet<>(); paths.add(path); @@ -722,6 +686,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } else { log.error("Fail update Resource [{}]", lwM2mResource); } + } @@ -869,8 +834,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler valueKvProto = new JsonObject(); Object finalvalueKvProto = valueKvProto; Gson gson = new GsonBuilder().create(); + ResourceModel.Type finalCurrentType = currentType; resourceValue.getValues().forEach((k, v) -> { - Object val = this.converter.convertValue(v, currentType, expectedType, + Object val = this.converter.convertValue(v, finalCurrentType, expectedType, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); JsonElement element = gson.toJsonTree(val, val.getClass()); ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element); @@ -880,6 +846,9 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); } + LwM2mOtaConvert lwM2mOtaConvert = convertOtaUpdateValueToString (pathIdVer, valueKvProto, currentType); + valueKvProto = lwM2mOtaConvert.getValue(); + currentType = lwM2mOtaConvert.getCurrentType(); return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null; } } catch (Exception e) { @@ -1072,7 +1041,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param parametersNew - * @return ResultsAnalyzerParameters: add && new */ - private ResultsAnalyzerParameters getAnalyzerParameters(Set parametersOld, Set parametersNew) { + private ResultsAnalyzerParameters getAnalyzerParameters + (Set parametersOld, Set parametersNew) { ResultsAnalyzerParameters analyzerParameters = null; if (!parametersOld.equals(parametersNew)) { analyzerParameters = new ResultsAnalyzerParameters(); @@ -1084,7 +1054,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler return analyzerParameters; } - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set parametersObserve, Set parameters) { + private ResultsAnalyzerParameters getAnalyzerParametersIn + (Set parametersObserve, Set parameters) { ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); analyzerParameters.setPathPostParametersAdd(parametersObserve .stream().filter(parameters::contains).collect(Collectors.toSet())); @@ -1113,7 +1084,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler }); } - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap keyNameOld, ConcurrentHashMap keyNameNew) { + private ResultsAnalyzerParameters getAnalyzerKeyName + (ConcurrentHashMap keyNameOld, ConcurrentHashMap keyNameNew) { ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); Set paths = keyNameNew.entrySet() .stream() @@ -1133,7 +1105,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param attributeLwm2mNew - * @return */ - private void getAnalyzerAttributeLwm2m(Set registrationIds, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { + private void getAnalyzerAttributeLwm2m(Set registrationIds, JsonObject attributeLwm2mOld, JsonObject + attributeLwm2mNew) { ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); ConcurrentHashMap lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), new TypeToken>() { @@ -1199,7 +1172,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler ); } - private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { + private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String + path) { if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), path, WRITE_REPLACE, ContentFormat.TLV.getName(), valueNew, @@ -1245,7 +1219,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param attributesResponse - * @param sessionInfo - */ - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { + public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg + attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { try { List tsKvProtos = attributesResponse.getSharedAttributeListList(); this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo); @@ -1264,7 +1239,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param tsKvProtos * @param sessionInfo */ - public void updateAttributeFromThingsboard(List tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { + public void updateAttributeFromThingsboard + (List tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { LwM2mClient lwM2MClient = clientContext.getClient(sessionInfo); if (lwM2MClient != null) { log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); @@ -1327,8 +1303,10 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler * @param sessionInfo - */ private void reportActivityAndRegister(SessionInfoProto sessionInfo) { - if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + if (sessionInfo != null) { + if (transportService.reportActivity(sessionInfo) == null) { + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); + } this.reportActivitySubscription(sessionInfo); } } @@ -1365,8 +1343,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { - if (lwM2MClient.getRegistration().getSupportedVersion(FW_ID) != null) { + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { + if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { DefaultLwM2MTransportMsgHandler handler = this; @@ -1376,20 +1354,21 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.FIRMWARE.name())) { - log.warn("7) firmware start with ver: [{}]", response.getVersion()); - lwM2MClient.setFwUpdate(new LwM2mFwSwUpdate(lwM2MClient, OtaPackageType.FIRMWARE)); -// clientContext.getProfile(lwM2MClient.getProfileId()).getPostAttributeLwm2mProfile(); - lwM2MClient.getFwUpdate().setTypeUpdateByURL(true); - lwM2MClient.getFwUpdate().setRpcRequest(rpcRequest); - lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion()); - lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle()); - log.warn("11) OtaPackageIdMSB: [{}] OtaPackageIdLSB: [{}]", response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); - lwM2MClient.getFwUpdate().setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); - - if (rpcRequest == null) { - lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest); - } else { - lwM2MClient.getFwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); + if (!FAILED.name().equals(fwUpdate.getStateUpdate())) { + log.warn("7) firmware start with ver: [{}]", response.getVersion()); + fwUpdate.setRpcRequest(rpcRequest); + fwUpdate.setCurrentVersion(response.getVersion()); + fwUpdate.setCurrentTitle(response.getTitle()); + fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); + if (rpcRequest == null) { + fwUpdate.sendReadObserveInfo(lwM2mTransportRequest); + } else { + fwUpdate.writeFwSwWare(handler, lwM2mTransportRequest); + } + } + else { + log.warn("7_1) OtaPackage [{}] [{}] [{}]", lwM2MClient.getDeviceName(), response.getVersion(), fwUpdate.getStateUpdate()); } } else { log.trace("OtaPackage [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); @@ -1405,7 +1384,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, Lwm2mClientRpcRequest rpcRequest) { + public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(lwM2MClient); if (sessionInfo != null) { @@ -1416,18 +1395,16 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) && response.getType().equals(OtaPackageType.SOFTWARE.name())) { - lwM2MClient.setSwUpdate(new LwM2mFwSwUpdate(lwM2MClient, OtaPackageType.SOFTWARE)); -// clientContext.getProfile(lwM2MClient.getProfileId()).getPostAttributeLwm2mProfile(); - lwM2MClient.getSwUpdate().setTypeUpdateByURL(false); - lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); - lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); - lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); - lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + LwM2mFwSwUpdate swUpdate = lwM2MClient.getSwUpdate(clientContext); + swUpdate.setRpcRequest(rpcRequest); + swUpdate.setCurrentVersion(response.getVersion()); + swUpdate.setCurrentTitle(response.getTitle()); + swUpdate.setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); + swUpdate.sendReadObserveInfo(lwM2mTransportRequest); if (rpcRequest == null) { - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); + swUpdate.sendReadObserveInfo(lwM2mTransportRequest); } else { - lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); + swUpdate.writeFwSwWare(handler, lwM2mTransportRequest); } } else { log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); @@ -1443,7 +1420,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } } - private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String + nameFwSW) { return TransportProtos.GetOtaPackageRequestMsg.newBuilder() .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java index 688ab88eeb..c0afe4fa81 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2mTransportService.java @@ -63,8 +63,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_COAP_RESOURCE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.OTA_COAP_RESOURCE; @Slf4j @Component @@ -104,10 +103,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { */ - LwM2mTransportCoapResource fwCoapResource = new LwM2mTransportCoapResource(handler, FW_COAP_RESOURCE); - LwM2mTransportCoapResource swCoapResource = new LwM2mTransportCoapResource(handler, SW_COAP_RESOURCE); - this.server.coap().getServer().add(fwCoapResource); - this.server.coap().getServer().add(swCoapResource); + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, OTA_COAP_RESOURCE); + this.server.coap().getServer().add(otaCoapResource); this.startLhServer(); this.context.setServer(server); } @@ -138,7 +135,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); /* Create CoAP Config */ - builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort())); + builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort(), config)); /* Define model provider (Create Models )*/ LwM2mModelProvider modelProvider = new LwM2mVersionedModelProvider(this.lwM2mClientContext, this.helper, this.context); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java index 280cb7dde4..9e07b0b6db 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mNetworkConfig.java @@ -16,10 +16,12 @@ package org.thingsboard.server.transport.lwm2m.server; import org.eclipse.californium.core.network.config.NetworkConfig; +import org.eclipse.californium.core.network.config.NetworkConfigDefaults; +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; public class LwM2mNetworkConfig { - public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort, LwM2MTransportServerConfig config) { NetworkConfig coapConfig = new NetworkConfig(); coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec); coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort); @@ -41,7 +43,7 @@ public class LwM2mNetworkConfig { CoAP client will try to use block mode or adapt the block size when receiving a 4.13 Entity too large response code */ - coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, config.isBlock2OptionEnable()); /** Property to indicate if the response should always include the Block2 option \ when client request early blockwise negociation but the response can be sent on one packet. @@ -49,8 +51,15 @@ public class LwM2mNetworkConfig { - value of true indicate that the server will response with block2 option event if no further blocks are required. */ coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); - - coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000); + /** + * The maximum amount of time (in milliseconds) allowed between + * transfers of individual blocks in a blockwise transfer before the + * blockwise transfer state is discarded. + *

+ * The default value of this property is + * {@link NetworkConfigDefaults#DEFAULT_BLOCKWISE_STATUS_LIFETIME} = 5 * 60 * 1000; // 5 mins [ms]. + */ + coapConfig.setLong(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, config.getBlockwiseLifetime()); /** !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 The maximum size of a resource body (in bytes) that will be accepted diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java new file mode 100644 index 0000000000..7b7c56adb3 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOtaConvert.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2021 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.transport.lwm2m.server; + +import lombok.Data; +import org.eclipse.leshan.core.model.ResourceModel; + +@Data +public class LwM2mOtaConvert { + private ResourceModel.Type currentType; + private Object value; +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index f2aa4dbf24..7ba491d6d1 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -30,11 +30,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_COAP_RESOURCE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_COAP_RESOURCE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.OTA_COAP_RESOURCE; @Slf4j -public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { +public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { private final ConcurrentMap tokenToObserveRelationMap = new ConcurrentHashMap<>(); private final ConcurrentMap tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); private final LwM2mTransportMsgHandler handler; @@ -68,52 +67,11 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { @Override protected void processHandleGet(CoapExchange exchange) { - log.warn("1) processHandleGet [{}]", exchange); - // exchange.respond(CoAP.ResponseCode.BAD_REQUEST); - int ver = 10; -// int ver = 9; - UUID currentId; - if (ver == 10) { - long mSB = 4951557297924280811L; - long lSb = -8451242882176289074L; - currentId = new UUID(mSB, lSb); - } else { - long mSB = 9085827945869414891L; - long lSb = -9086716326447629319L; - currentId = new UUID(mSB, lSb); + log.warn("90) processHandleGet [{}]", exchange); + if (exchange.getRequestOptions().getUriPath().size() == 2 && + OTA_COAP_RESOURCE.equals(exchange.getRequestOptions().getUriPath().get(0))) { + this.sentOtaData(exchange); } - - String coapResource = exchange.getRequestOptions().getUriPath().get(0); - String token = exchange.getRequestOptions().getUriPath().get(1); - if (exchange.getRequestOptions().getBlock2() != null) { - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - int chunk = 0; - Response response = new Response(CoAP.ResponseCode.CONTENT); - byte[] fwData = this.getFwData(currentId); - if (fwData != null && fwData.length > 0) { - response.setPayload(fwData); - boolean moreFlag = fwData.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, chunk); - exchange.respond(response); - } - - } -// List options = exchange.advanced().getRequest().getOptions().getUriPath(); -// options.stream() -// .filter(o -> FW_COAP_RESOURCE.equals(o)) -// .findFirst() -// .ifPresent(o -> System.err.println(o.getNumber() + " " + o.getStringValue())); - - if (FW_COAP_RESOURCE.equals(coapResource)) { - - } - else if (SW_COAP_RESOURCE.equals(coapResource)) { - - } - - - - } @Override @@ -169,7 +127,26 @@ public class LwM2mTransportCoapResource extends AbstractLwm2mTransportResource { } } - private byte[] getFwData(UUID currentId) { + private void sentOtaData (CoapExchange exchange) { + String idStr = exchange.getRequestOptions().getUriPath().get(1); + UUID currentId = UUID.fromString(idStr); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + int chunk = 0; + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = this.getOtaData(currentId); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + boolean moreFlag = fwData.length > chunkSize; + response.getOptions().setBlock2(chunkSize, moreFlag, chunk); + log.warn("91) Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); + exchange.respond(response); + } + + } + } + + private byte[] getOtaData(UUID currentId) { return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java index 794df65db5..0ce018303c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java @@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; -import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import java.util.Collection; import java.util.Optional; @@ -39,7 +39,7 @@ public interface LwM2mTransportMsgHandler { void setCancelObservationsAll(Registration registration); - void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest); void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java index 98b40e74fc..f0484d521c 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java @@ -55,7 +55,7 @@ import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; -import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import javax.annotation.PostConstruct; @@ -75,7 +75,7 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWN import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; @@ -126,7 +126,7 @@ public class LwM2mTransportRequest { */ public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, - String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest lwm2mClientRpcRequest) { + String contentFormatName, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { try { String target = convertPathFromIdVerToObjectId(targetIdVer); ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; @@ -143,7 +143,7 @@ public class LwM2mTransportRequest { } catch (ClientSleepingException e) { DownlinkRequest finalRequest = request; long finalTimeoutInMs = timeoutInMs; - Lwm2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; + LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); } catch (Exception e) { log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); @@ -225,7 +225,7 @@ public class LwM2mTransportRequest { private DownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, ContentFormat contentFormat, String target, String targetIdVer, - LwM2mPath resultIds, Object params, Lwm2mClientRpcRequest rpcRequest) { + LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { DownlinkRequest request = null; switch (typeOper) { case READ: @@ -330,7 +330,7 @@ public class LwM2mTransportRequest { @SuppressWarnings({"error sendRequest"}) private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, - long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { + long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { context.getServer().send(registration, request, timeoutInMs, (ResponseCallback) response -> { if (!lwM2MClient.isInit()) { @@ -354,13 +354,13 @@ public class LwM2mTransportRequest { /** Not Found set setClient_fw_info... = empty **/ - if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); } if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { @@ -371,13 +371,13 @@ public class LwM2mTransportRequest { /** version == null set setClient_fw_info... = empty **/ - if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { + if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); } - if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); } if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { @@ -389,7 +389,7 @@ public class LwM2mTransportRequest { String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); handler.sendLogsToThingsboard(msg, registration.getId()); - log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); + log.error(" [{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); if (rpcRequest != null) { handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); } @@ -398,7 +398,7 @@ public class LwM2mTransportRequest { private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, Integer resourceId, Object value, ResourceModel.Type type, - Registration registration, Lwm2mClientRpcRequest rpcRequest) { + Registration registration, LwM2mClientRpcRequest rpcRequest) { try { if (type != null) { switch (type) { @@ -444,7 +444,7 @@ public class LwM2mTransportRequest { } private void handleResponse(Registration registration, final String path, LwM2mResponse response, - DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { + DownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { responseRequestExecutor.submit(() -> { try { this.sendResponse(registration, path, response, request, rpcRequest); @@ -462,7 +462,7 @@ public class LwM2mTransportRequest { * @param response - */ private void sendResponse(Registration registration, String path, LwM2mResponse response, - DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { + DownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); String msgLog = ""; if (response instanceof ReadResponse) { @@ -509,7 +509,7 @@ public class LwM2mTransportRequest { } } - private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { + private void infoWriteResponse(Registration registration, LwM2mResponse response, DownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { try { LwM2mNode node = ((WriteRequest) request).getNode(); String msg = null; @@ -546,7 +546,7 @@ public class LwM2mTransportRequest { } if (msg != null) { handler.sendLogsToThingsboard(msg, registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { this.afterWriteSuccessFwSwUpdate(registration, request); if (rpcRequest != null) { rpcRequest.setInfoMsg(msg); @@ -568,7 +568,7 @@ public class LwM2mTransportRequest { */ private void afterWriteSuccessFwSwUpdate(Registration registration, DownlinkRequest request) { LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); } @@ -583,7 +583,7 @@ public class LwM2mTransportRequest { */ private void afterWriteFwSWUpdateError(Registration registration, DownlinkRequest request, String msgError) { LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); - if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) { + if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } @@ -603,7 +603,7 @@ public class LwM2mTransportRequest { } } - private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) { + private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { handler.sendLogsToThingsboard(observeCancelMsg, registration.getId()); log.warn("[{}]", observeCancelMsg); if (rpcRequest != null) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java index 2e7681250a..9be2c24053 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportUtil.java @@ -89,6 +89,11 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.VERI @Slf4j public class LwM2mTransportUtil { + public static final String EVENT_AWAKE = "AWAKE"; + public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ"; + public static final String RESPONSE_CHANNEL = "RESP"; + public static final String OBSERVE_CHANNEL = "OBSERVE"; + public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; public static final String BOOTSTRAP = "bootstrap"; @@ -116,9 +121,6 @@ public class LwM2mTransportUtil { public static final String LOG_LW2M_WARN = "warn"; public static final String LOG_LW2M_VALUE = "value"; - public static final int LWM2M_STRATEGY_1 = 1; - public static final int LWM2M_STRATEGY_2 = 2; - public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; public static final String LWM2M_VERSION_DEFAULT = "1.0"; @@ -132,19 +134,22 @@ public class LwM2mTransportUtil { public static final String FINISH_VALUE_KEY = ","; public static final String START_JSON_KEY = "{"; public static final String FINISH_JSON_KEY = "}"; - // public static final String contentFormatNameKey = "contentFormatName"; public static final String INFO_KEY = "info"; - // public static final String TIME_OUT_IN_MS = "timeOutInMs"; public static final String RESULT_KEY = "result"; public static final String ERROR_KEY = "error"; public static final String METHOD_KEY = "methodName"; + public static final String OTA_COAP_RESOURCE = "firmwarePackage"; // Firmware - public static final String FW_COAP_RESOURCE = "fw"; public static final String FW_UPDATE = "Firmware update"; - public static final Integer FW_ID = 5; + public static final Integer FW_5_ID = 5; + public static final Integer FW_19_ID = 19; + // Package W - public static final String FW_PACKAGE_ID = "/5/0/0"; + public static final String FW_PACKAGE_5_ID = "/5/0/0"; + public static final String FW_PACKAGE_19_ID = "/19/0/0"; + // Package URI + public static final String FW_PACKAGE_URI_ID = "/5/0/1"; // State R public static final String FW_STATE_ID = "/5/0/3"; // Update Result R @@ -152,16 +157,27 @@ public class LwM2mTransportUtil { // PkgName R public static final String FW_NAME_ID = "/5/0/6"; // PkgVersion R - public static final String FW_VER_ID = "/5/0/7"; + public static final String FW_5_VER_ID = "/5/0/7"; + /** + * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 + * BC68JAR01A10 + * # Request prodct type number + * ATI + * Quectel + * BC68 + * Revision:BC68JAR01A10 + */ + public static final String FW_3_VER_ID = "/3/0/3"; // Update E public static final String FW_UPDATE_ID = "/5/0/2"; // Software - public static final String SW_COAP_RESOURCE = "sw"; public static final String SW_UPDATE = "Software update"; public static final Integer SW_ID = 9; // Package W public static final String SW_PACKAGE_ID = "/9/0/2"; + // Package URI + public static final String SW_PACKAGE_URI_ID = "/9/0/3"; // Update State R public static final String SW_UPDATE_STATE_ID = "/9/0/7"; // Update Result R @@ -234,7 +250,7 @@ public class LwM2mTransportUtil { DELETE(11, "Delete"), // only for RPC - FW_UPDATE(12,"FirmwareUpdate"); + FW_UPDATE(12, "FirmwareUpdate"); // FW_READ_INFO(12, "FirmwareReadInfo"), // SW_READ_INFO(15, "SoftwareReadInfo"), @@ -357,20 +373,11 @@ public class LwM2mTransportUtil { * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED */ - public static OtaPackageUpdateStatus EqualsFwSateToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { + public static OtaPackageUpdateStatus equalsFwSateFwResultToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { switch (updateResultFw) { case INITIAL: - switch (stateFw) { - case IDLE: - return VERIFIED; - case DOWNLOADING: - return DOWNLOADING; - case DOWNLOADED: - return DOWNLOADED; - case UPDATING: - return UPDATING; - } - case UPDATE_SUCCESSFULLY: + return equalsFwSateToFirmwareUpdateStatus(stateFw); + case UPDATE_SUCCESSFULLY: return UPDATED; case NOT_ENOUGH: case OUT_OFF_MEMORY: @@ -386,6 +393,41 @@ public class LwM2mTransportUtil { } } + public static OtaPackageUpdateStatus equalsFwResultToFirmwareUpdateStatus(UpdateResultFw updateResultFw) { + switch (updateResultFw) { + case INITIAL: + return VERIFIED; + case UPDATE_SUCCESSFULLY: + return UPDATED; + case NOT_ENOUGH: + case OUT_OFF_MEMORY: + case CONNECTION_LOST: + case INTEGRITY_CHECK_FAILURE: + case UNSUPPORTED_TYPE: + case INVALID_URI: + case UPDATE_FAILED: + case UNSUPPORTED_PROTOCOL: + return FAILED; + default: + throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); + } + } + + public static OtaPackageUpdateStatus equalsFwSateToFirmwareUpdateStatus(StateFw stateFw) { + switch (stateFw) { + case IDLE: + return VERIFIED; + case DOWNLOADING: + return DOWNLOADING; + case DOWNLOADED: + return DOWNLOADED; + case UPDATING: + return UPDATING; + default: + throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", stateFw); + } + } + /** * SW Update State R * 0: INITIAL Before downloading. (see 5.1.2.1) @@ -499,6 +541,101 @@ public class LwM2mTransportUtil { } } + public enum LwM2MClientStrategy { + CLIENT_STRATEGY_1(1, "Read only resources marked as observation"), + CLIENT_STRATEGY_2(2, "Read all client resources"); + + public int code; + public String type; + + LwM2MClientStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MClientStrategy fromStrategyClientByType(String type) { + for (LwM2MClientStrategy to : LwM2MClientStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported Client Strategy type : %s", type)); + } + + public static LwM2MClientStrategy fromStrategyClientByCode(int code) { + for (LwM2MClientStrategy to : LwM2MClientStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported Client Strategy code : %s", code)); + } + } + + public enum LwM2MFirmwareUpdateStrategy { + OBJ_5_BINARY(1, "ObjectId 5, Binary"), + OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), + OBJ_19_BINARY(3, "ObjectId 19, Binary"); + + public int code; + public String type; + + LwM2MFirmwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); + } + + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); + } + } + + public enum LwM2MSoftwareUpdateStrategy { + BINARY(1, "ObjectId 9, Binary"), + TEMP_URL(2, "ObjectId 9, URI"); + + public int code; + public String type; + + LwM2MSoftwareUpdateStrategy(int code, String type) { + this.code = code; + this.type = type; + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.type.equals(type)) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); + } + + public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { + if (to.code == code) { + return to; + } + } + throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); + } + + } + /** * FirmwareUpdateStatus { * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED @@ -536,10 +673,6 @@ public class LwM2mTransportUtil { } } - public static final String EVENT_AWAKE = "AWAKE"; - public static final String RESPONSE_REQUEST_CHANNEL = "RESP_REQ"; - public static final String RESPONSE_CHANNEL = "RESP"; - public static final String OBSERVE_CHANNEL = "OBSERVE"; public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { @@ -560,6 +693,36 @@ public class LwM2mTransportUtil { } } + public static LwM2mOtaConvert convertOtaUpdateValueToString (String pathIdVer, Object value, ResourceModel.Type currentType) { + String path = convertPathFromIdVerToObjectId(pathIdVer); + LwM2mOtaConvert lwM2mOtaConvert = new LwM2mOtaConvert(); + if (path != null) { + if (FW_STATE_ID.equals(path)) { + lwM2mOtaConvert.setCurrentType(STRING); + lwM2mOtaConvert.setValue(StateFw.fromStateFwByCode(((Long) value).intValue()).type); + return lwM2mOtaConvert; + } + else if (FW_RESULT_ID.equals(path)) { + lwM2mOtaConvert.setCurrentType(STRING); + lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type); + return lwM2mOtaConvert; + } + else if (SW_UPDATE_STATE_ID.equals(path)) { + lwM2mOtaConvert.setCurrentType(STRING); + lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type); + return lwM2mOtaConvert; + } + else if (SW_RESULT_ID.equals(path)) { + lwM2mOtaConvert.setCurrentType(STRING); + lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type); + return lwM2mOtaConvert; + } + } + lwM2mOtaConvert.setCurrentType(currentType); + lwM2mOtaConvert.setValue(value); + return lwM2mOtaConvert; + } + public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) { if (content instanceof LwM2mObject) { return (LwM2mObject) content; @@ -635,11 +798,6 @@ public class LwM2mTransportUtil { return null; } - public static int getClientOnlyObserveAfterConnect(LwM2mClientProfile profile) { - return profile.getPostClientLwM2mSettings().getAsJsonObject().has("clientOnlyObserveAfterConnect") ? - profile.getPostClientLwM2mSettings().getAsJsonObject().get("clientOnlyObserveAfterConnect").getAsInt() : 1; - } - private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) { return (objectMsg != null && !objectMsg.isJsonNull() && @@ -916,7 +1074,7 @@ public class LwM2mTransportUtil { case GREATER_THAN: case LESSER_THAN: case STEP: - if (value.getClass().getSimpleName().equals("String") ) { + if (value.getClass().getSimpleName().equals("String")) { value = Double.valueOf((String) value); } return serviceImpl.converter.convertValue(value, equalsResourceTypeGetSimpleName(value), FLOAT, new LwM2mPath(target)); @@ -930,11 +1088,11 @@ public class LwM2mTransportUtil { return new Attribute(key, attrValue); } catch (Exception e) { log.error("CreateAttribute, not valid parameter key: [{}], attrValue: [{}], error: [{}]", key, attrValue, e.getMessage()); - return null; + return null; } } - public static boolean isFwSwWords (String pathName) { + public static boolean isFwSwWords(String pathName) { return OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) || OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.CHECKSUM).equals(pathName) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 602596ed2b..1ebc9ac66e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -31,6 +31,7 @@ import org.eclipse.leshan.server.registration.Registration; import org.eclipse.leshan.server.security.SecurityInfo; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; @@ -361,5 +362,21 @@ public class LwM2mClient implements Cloneable { } } + public LwM2mFwSwUpdate getFwUpdate (LwM2mClientContext clientContext) { + if (this.fwUpdate == null) { + LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); + this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, lwM2mClientProfile.getFwUpdateStrategy()); + } + return this.fwUpdate; + } + + public LwM2mFwSwUpdate getSwUpdate (LwM2mClientContext clientContext) { + if (this.swUpdate == null) { + LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); + this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, lwM2mClientProfile.getSwUpdateStrategy()); + } + return this.fwUpdate; + } + } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java index 19af453f3c..67edbf2747 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java @@ -20,15 +20,23 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; import lombok.Data; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MClientStrategy; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MSoftwareUpdateStrategy; @Data public class LwM2mClientProfile { + private final String clientStrategyStr = "clientStrategy"; + private final String fwUpdateStrategyStr = "fwUpdateStrategy"; + private final String swUpdateStrategyStr = "swUpdateStrategy"; private TenantId tenantId; /** - * {"clientLwM2mSettings": { - * clientUpdateValueAfterConnect: false; - * } + * "clientLwM2mSettings": { + * "fwUpdateStrategy": "1", + * "swUpdateStrategy": "1", + * "clientStrategy": "1" + * } **/ private JsonObject postClientLwM2mSettings; @@ -85,5 +93,21 @@ public class LwM2mClientProfile { } } + public int getClientStrategy() { + return this.postClientLwM2mSettings.getAsJsonObject().has(this.clientStrategyStr) ? + Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.clientStrategyStr).getAsString()) : + LwM2MClientStrategy.CLIENT_STRATEGY_1.code; + } + public int getFwUpdateStrategy() { + return this.postClientLwM2mSettings.getAsJsonObject().has(this.fwUpdateStrategyStr) ? + Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.fwUpdateStrategyStr).getAsString()) : + LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code; + } + + public int getSwUpdateStrategy() { + return this.postClientLwM2mSettings.getAsJsonObject().has(this.swUpdateStrategyStr) ? + Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.swUpdateStrategyStr).getAsString()) : + LwM2MSoftwareUpdateStrategy.BINARY.code; + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java similarity index 98% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java index d71c6b27f6..ca831b1413 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/Lwm2mClientRpcRequest.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java @@ -57,7 +57,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.v @Slf4j @Data -public class Lwm2mClientRpcRequest { +public class LwM2mClientRpcRequest { private Registration registration; private TransportProtos.SessionInfoProto sessionInfo; @@ -75,10 +75,10 @@ public class Lwm2mClientRpcRequest { private String infoMsg; private String responseCode; - public Lwm2mClientRpcRequest() { + public LwM2mClientRpcRequest() { } - public Lwm2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, + public LwM2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) { this.registration = registration; this.sessionInfo = sessionInfo; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 9cafc8f792..97496b91ce 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -20,6 +20,7 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.eclipse.leshan.core.request.ContentFormat; +import org.eclipse.leshan.server.registration.Registration; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.gen.transport.TransportProtos; @@ -36,20 +37,31 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; +import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.OTA_COAP_RESOURCE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; @@ -59,6 +71,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; @Slf4j @@ -106,27 +119,29 @@ public class LwM2mFwSwUpdate { private final List pendingInfoRequestsStart; @Getter @Setter - private volatile Lwm2mClientRpcRequest rpcRequest; + private volatile LwM2mClientRpcRequest rpcRequest; @Getter @Setter - private volatile boolean typeUpdateByURL; + private volatile int updateStrategy; - public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type) { + public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { this.lwM2MClient = lwM2MClient; this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); this.type = type; this.stateUpdate = null; - this.typeUpdateByURL = false; + this.updateStrategy = updateStrategy; this.initPathId(); } private void initPathId() { if (FIRMWARE.equals(this.type)) { - this.pathPackageId = FW_PACKAGE_ID; + this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? + FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? + FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; this.pathStateId = FW_STATE_ID; this.pathResultId = FW_RESULT_ID; this.pathNameId = FW_NAME_ID; - this.pathVerId = FW_VER_ID; + this.pathVerId = FW_5_VER_ID; this.pathInstallId = FW_UPDATE_ID; this.wUpdate = FW_UPDATE; } else if (SOFTWARE.equals(this.type)) { @@ -147,13 +162,13 @@ public class LwM2mFwSwUpdate { } if (this.pendingInfoRequestsStart.size() == 0) { this.infoFwSwUpdate = false; - if (!OtaPackageUpdateStatus.DOWNLOADING.name().equals(this.stateUpdate)) { - boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() : - this.conditionalSwUpdateStart(); - if (conditionalStart) { - this.writeFwSwWare(handler, request); - } +// if (!FAILED.name().equals(this.stateUpdate)) { + boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) : + this.conditionalSwUpdateStart(handler); + if (conditionalStart) { + this.writeFwSwWare(handler, request); } +// } } } @@ -163,31 +178,42 @@ public class LwM2mFwSwUpdate { */ public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { if (this.currentId != null) { - this.stateUpdate = OtaPackageUpdateStatus.DOWNLOADING.name(); + this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); - int chunkSize = 0; - int chunk = 0; - byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, - LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), FW_PACKAGE_ID); + LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), this.pathPackageId); handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); - request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), - firmwareChunk, handler.config.getTimeout(), this.rpcRequest); + if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { + int chunkSize = 0; + int chunk = 0; + byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); + request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(), + firmwareChunk, handler.config.getTimeout(), this.rpcRequest); + } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { + Registration registration = this.getLwM2MClient().getRegistration(); + String api = handler.config.getHostRequests(); + int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); + String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + OTA_COAP_RESOURCE + "/" + this.currentId.toString(); + request.sendAllRequest(this.lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, null, + uri, handler.config.getTimeout(), this.rpcRequest); + } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { + + } } else { String msgError = "FirmWareId is null."; log.warn("6) [{}]", msgError); if (this.rpcRequest != null) { handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); } - log.error (msgError); + log.error(msgError); this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); } } public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) { - this.sendSateOnThingsBoard(handler); +// this.sendSateOnThingsBoard(handler); String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); if (LOG_LW2M_ERROR.equals(typeInfo)) { @@ -203,32 +229,50 @@ public class LwM2mFwSwUpdate { * send execute */ public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { - this.setStateUpdate(UPDATING.name()); this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(), null, 0, this.rpcRequest); } /** - * Firmware start: + * Firmware start: Check if the version has changed and launch a new update. + * -ObjectId 5, Binary or ObjectId 5, URI * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. * - We launch the update regardless of the state of the firmware and its version. - * -- If the result of the update is not errors (equal to 1 or 0) and ver is not empty - This means that before the update has passed. - * -- If the result of the update is not errors and is empty - This means that there has not been an update yet. - * - Check if the version has changed and launch a new update. + * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. + * * ObjectId 5, Binary + * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. + * Compare current versions by equals. + * * ObjectId 5, URI + * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. + * Compare current versions by contains. */ - private boolean conditionalFwUpdateStart() { + private boolean conditionalFwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); + String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId); + String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId); + String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID); // #1/#2 - return updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code || - ( - (updateResultFw <= LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code - ) && - ( - (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) || - (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId))) - ) - ); + String fwMsg = null; + if ((this.currentVersion != null && ( + ver5 != null && ver5.equals(this.currentVersion) || + ver3 != null && ver3.contains(this.currentVersion) + )) || + (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { + fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LW2M_ERROR, + this.currentVersion); + } + else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LW2M_ERROR, + updateResultFw); + } + if (fwMsg != null) { + handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); + return false; + } + else { + return true; + } } @@ -252,7 +296,7 @@ public class LwM2mFwSwUpdate { /** * After operation Execute success inspection Update Result : - * > 1 error: "Firmware updated successfully" + * > 1 error: "Firmware updated successfully" */ public boolean conditionalFwExecuteAfterError() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); @@ -268,7 +312,7 @@ public class LwM2mFwSwUpdate { * - If Update Result is not errors and ver is not empty - This means that before unInstall update * * - Check if the version has changed and launch a new update. */ - private boolean conditionalSwUpdateStart() { + private boolean conditionalSwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); // #1/#2 return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || @@ -296,7 +340,7 @@ public class LwM2mFwSwUpdate { * -- inspection Update Result: * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.") * -- fw_state/sw_state = UPDATED - * + *

* After finish operation Execute (error): * -- inspection updateResult and send to thingsboard info about error * --- send to telemetry ( key - this is name Update Result in model) ( @@ -329,7 +373,7 @@ public class LwM2mFwSwUpdate { /** * After operation Execute success inspection Update Result : - * >= 50 - error "NOT_ENOUGH_STORAGE" + * >= 50 - error "NOT_ENOUGH_STORAGE" */ public boolean conditionalSwExecuteAfterError() { Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); @@ -358,18 +402,75 @@ public class LwM2mFwSwUpdate { public void sendReadObserveInfo(LwM2mTransportRequest request) { this.infoFwSwUpdate = true; - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( - this.pathVerId, this.lwM2MClient.getRegistration())); - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( - this.pathNameId, this.lwM2MClient.getRegistration())); this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pathStateId, this.lwM2MClient.getRegistration())); this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( this.pathResultId, this.lwM2MClient.getRegistration())); + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + FW_3_VER_ID, this.lwM2MClient.getRegistration())); + if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || + LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || + SOFTWARE.equals(this.type)) { + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pathVerId, this.lwM2MClient.getRegistration())); + this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( + this.pathNameId, this.lwM2MClient.getRegistration())); + } this.pendingInfoRequestsStart.forEach(pathIdVer -> { request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(), null, 0, this.rpcRequest); }); } + + /** + * Before operation Execute (FwUpdate) inspection Update Result : + * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED + * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value + * - start Execute (FwUpdate) + * After finished operation Execute (FwUpdate) inspection Update Result : + * - after start operation Execute (FwUpdate): fw_state = UPDATING + * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") + * - finished operation Execute (FwUpdate) + */ + public void updateStateOta(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, + Registration registration, String path, int value) { + if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { + if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { + if (DOWNLOADED.name().equals(this.getStateUpdate()) + && this.conditionalFwExecuteStart()) { + this.executeFwSwWare(handler, request); + } else if (UPDATING.name().equals(this.getStateUpdate()) + && this.conditionalFwExecuteAfterSuccess()) { + this.finishFwSwUpdate(handler, true); + } else if (UPDATING.name().equals(this.getStateUpdate()) + && this.conditionalFwExecuteAfterError()) { + this.finishFwSwUpdate(handler, false); + } + } + } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { + if ((convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { + String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); + if (!FAILED.name().equals(this.stateUpdate) && StringUtils.isNotEmpty(state) && !state.equals(this.stateUpdate)) { + this.stateUpdate = state; + this.sendSateOnThingsBoard(handler); + } + if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) { + this.executeFwSwWare(handler, request); + } + } + if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { + if (value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { + this.setStateUpdate(INITIATED.name()); + } else if (value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + this.setStateUpdate(UPDATED.name()); + } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + this.setStateUpdate(FAILED.name()); + } + this.sendSateOnThingsBoard(handler); + } + } else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) { + + } + } } diff --git a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml index 39acda4590..261d1c79f1 100644 --- a/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml +++ b/transport/lwm2m/src/main/resources/tb-lwm2m-transport.yml @@ -104,9 +104,13 @@ transport: server: id: "${LWM2M_SERVER_ID:123}" bind_address: "${LWM2M_BIND_ADDRESS:0.0.0.0}" + # the host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_REQUESTS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT:5685}" security: bind_address: "${LWM2M_BIND_ADDRESS_SECURITY:0.0.0.0}" + # the security host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_SECURITY_REQUESTS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_SECURITY:5686}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working public_x: "${LWM2M_SERVER_PUBLIC_X:05064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f358}" @@ -114,13 +118,18 @@ transport: private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}" # Only Certificate_x509: alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}" + skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" bootstrap: enable: "${LWM2M_ENABLED_BS:true}" id: "${LWM2M_SERVER_ID_BS:111}" bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + # the host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_REQUESTS_BS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_BS:5687}" security: - bind_address: "${LWM2M_BIND_ADDRESS_BS:0.0.0.0}" + bind_address: "${LWM2M_BIND_ADDRESS_SECURITY_BS:0.0.0.0}" + # the security host to receive requests to the server + bind_address_requests: "${LWM2M_BIND_ADDRESS_SECURITY_REQUESTS_BS:0.0.0.0}" bind_port: "${LWM2M_BIND_PORT_SECURITY_BS:5688}" # Only for RPK: Public & Private Key. If the keystore file is missing or not working public_x: "${LWM2M_SERVER_PUBLIC_X_BS:5017c87a1c1768264656b3b355434b0def6edb8b9bf166a4762d9930cd730f91}" @@ -139,6 +148,8 @@ transport: root_alias: "${LWM2M_SERVER_ROOT_CA:rootca}" enable_gen_new_key_psk_rpk: "${ENABLE_GEN_NEW_KEY_PSK_RPK:false}" timeout: "${LWM2M_TIMEOUT:120000}" + blockwise_lifetime: "${BLOCKWISE_LIFETIME:300000}" + block2_option_enable: "${BLOCK2_OPTION_ENABLED:true}" recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" @@ -150,6 +161,13 @@ transport: log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}" # Use redis for Security and Registration stores redis.enabled: "${LWM2M_REDIS_ENABLED:false}" + snmp: + enabled: "${SNMP_ENABLED:true}" + response_processing: + # parallelism level for executor (workStealingPool) that is responsible for handling responses from SNMP devices + parallelism_level: "${SNMP_RESPONSE_PROCESSING_PARALLELISM_LEVEL:20}" + # to configure SNMP to work over UDP or TCP + underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}" queue: type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index 7c3ec6cacf..b164e168cf 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -144,36 +144,36 @@

- {{ 'device-profile.lwm2m.client-only-observe-after-connect-label' | translate }} - {{ 'device-profile.lwm2m.client-strategy-label' | translate }} + - {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: + {{ 'device-profile.lwm2m.client-strategy' | translate: {count: 1} }} - {{ 'device-profile.lwm2m.client-only-observe-after-connect' | translate: + {{ 'device-profile.lwm2m.client-strategy' | translate: {count: 2} }}
- {{ 'device-profile.lwm2m.fw-update-url-label' | translate }} - - {{ 'device-profile.lwm2m.fw-update-url' | translate: + {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: {count: 1} }} - {{ 'device-profile.lwm2m.fw-update-url' | translate: + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: {count: 2} }} - {{ 'device-profile.lwm2m.fw-update-url' | translate: + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: {count: 3} }} - {{ 'device-profile.lwm2m.sw-update-url-label' | translate }} - - {{ 'device-profile.lwm2m.sw-update-url' | translate: + {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: {count: 1} }} - {{ 'device-profile.lwm2m.sw-update-url' | translate: + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: {count: 2} }} diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 3342d24791..886ed12c47 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -89,9 +89,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding:[], bootstrapServer: [null, Validators.required], lwm2mServer: [null, Validators.required], - clientOnlyObserveAfterConnect: [1, []], - fwUpdateUrl: [1, []], - swUpdateUrl: [1, []], + clientStrategy: [1, []], + fwUpdateStrategy: [1, []], + swUpdateStrategy: [1, []], }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] @@ -161,9 +161,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro binding: this.configurationValue.bootstrap.servers.binding, bootstrapServer: this.configurationValue.bootstrap.bootstrapServer, lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, - clientOnlyObserveAfterConnect: this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect, - fwUpdateUrl: this.configurationValue.clientLwM2mSettings.fwUpdateUrl, - swUpdateUrl: this.configurationValue.clientLwM2mSettings.swUpdateUrl + clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, + fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy, + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy }, {emitEvent: false}); } @@ -194,9 +194,9 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServers.defaultMinPeriod = config.defaultMinPeriod; bootstrapServers.notifIfDisabled = config.notifIfDisabled; bootstrapServers.binding = config.binding; - this.configurationValue.clientLwM2mSettings.clientOnlyObserveAfterConnect = config.clientOnlyObserveAfterConnect; - this.configurationValue.clientLwM2mSettings.fwUpdateUrl = config.fwUpdateUrl; - this.configurationValue.clientLwM2mSettings.swUpdateUrl = config.swUpdateUrl; + this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; + this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; + this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; this.upDateJsonAllConfig(); this.updateModel(); } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index af4bdc79b0..48613f58de 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -166,9 +166,9 @@ export interface Lwm2mProfileConfigModels { } export interface ClientLwM2mSettings { - clientOnlyObserveAfterConnect: string; - fwUpdateUrl: string; - swUpdateUrl: string; + clientStrategy: string; + fwUpdateStrategy: string; + swUpdateStrategy: string; } export interface ObservableAttributes { @@ -238,9 +238,9 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { - clientOnlyObserveAfterConnect: "1", - fwUpdateUrl: "1", - swUpdateUrl: "1" + clientStrategy: "1", + fwUpdateStrategy: "1", + swUpdateStrategy: "1" }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index d0209301fa..f6d86e4e84 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1247,13 +1247,13 @@ "bootstrap-server-account-timeout": "Account after the timeout", "bootstrap-server-account-timeout-tip": "Bootstrap-Server Account after the timeout value given by this resource.", "others-tab": "Other settings...", - "client-only-observe-after-connect-label": "Strategy", - "client-only-observe-after-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", - "client-only-observe-after-connect-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", - "fw-update-url-label": "Firmware update strategy", - "fw-update-url": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", - "sw-update-url-label": "Software update strategy", - "sw-update-url": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", + "client-strategy-label": "Strategy", + "client-strategy-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", + "client-strategy-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", + "fw-update-strategy-label": "Firmware update strategy", + "fw-update-strategy": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Auto-generate unique CoAP URL to download the package and push firmware update as Object 5 and Resource 1 (Package URI).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", + "sw-update-strategy-label": "Software update strategy", + "sw-update-strategy": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", "config-json-tab": "Json Config Profile Device" } }, From f38fc26dcbb0758815483488c2dfa94f3d0625ac Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Sun, 6 Jun 2021 22:31:54 +0300 Subject: [PATCH 49/75] LWM2M: merge with master --- .../lwm2m/server/DefaultLwM2MTransportMsgHandler.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index d82dbcbf08..10f2dfeff5 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -461,7 +461,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, requestUUID); String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); - if (!this.rpcSubscriptions.containsKey(requestUUID)) { +// if (!this.rpcSubscriptions.containsKey(requestUUID)) { this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); LwM2mClientRpcRequest lwm2mClientRpcRequest = null; try { @@ -486,7 +486,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler } this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); } - } +// } } private void checkRpcRequestTimeout() { From 99dc49db26b70ab5af397a0c95b19a73c1c7bddd Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Mon, 7 Jun 2021 08:56:09 +0300 Subject: [PATCH 50/75] LWM2M: six bug send log fw_state --- .../transport/lwm2m/server/client/LwM2mFwSwUpdate.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 97496b91ce..410d5a2e9a 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -449,9 +449,9 @@ public class LwM2mFwSwUpdate { } } } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { - if ((convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { + if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); - if (!FAILED.name().equals(this.stateUpdate) && StringUtils.isNotEmpty(state) && !state.equals(this.stateUpdate)) { + if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { this.stateUpdate = state; this.sendSateOnThingsBoard(handler); } @@ -460,9 +460,9 @@ public class LwM2mFwSwUpdate { } } if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { - if (value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { + if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { this.setStateUpdate(INITIATED.name()); - } else if (value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { + } else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(UPDATED.name()); } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { this.setStateUpdate(FAILED.name()); From b67d9930d3da0580680ba29c03d49a7ba638a44b Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 7 Jun 2021 10:49:04 +0300 Subject: [PATCH 51/75] UI: Refactoring --- .../ota-update/ota-update-table-config.resolve.ts | 9 +++------ .../pages/ota-update/ota-update.component.html | 14 +++++++++++--- .../home/pages/ota-update/ota-update.component.ts | 11 +++++++++++ .../components/button/copy-button.component.html | 3 --- .../components/button/copy-button.component.ts | 13 +++---------- .../src/assets/locale/locale.constant-en_US.json | 6 ++++-- 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts index dc7c36371d..19b84cef15 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts @@ -36,8 +36,6 @@ import { PageLink } from '@shared/models/page/page-link'; import { OtaUpdateComponent } from '@home/pages/ota-update/ota-update.component'; import { EntityAction } from '@home/models/entity/entity-component.models'; import { FileSizePipe } from '@shared/pipe/file-size.pipe'; -import { ClipboardService } from 'ngx-clipboard'; -import { ActionNotificationShow } from '@core/notification/notification.actions'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; @@ -51,8 +49,7 @@ export class OtaUpdateTableConfigResolve implements Resolve, private otaPackageService: OtaPackageService, - private fileSize: FileSizePipe, - private clipboardService: ClipboardService) { + private fileSize: FileSizePipe) { this.config.entityType = EntityType.OTA_PACKAGE; this.config.entityComponent = OtaUpdateComponent; this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE); @@ -67,11 +64,11 @@ export class OtaUpdateTableConfigResolve implements Resolve('type', 'ota-update.package-type', '20%', entity => { return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); }), - new EntityTableColumn('url', 'ota-update.url', '20%', entity => { + new EntityTableColumn('url', 'ota-update.direct-url', '20%', entity => { return entity.url && entity.url.length > 20 ? `${entity.url.slice(0, 20)}…` : ''; }, () => ({}), true, () => ({}), () => undefined, false, { - name: this.translate.instant('ota-update.copy-checksum'), + name: this.translate.instant('ota-update.copy-direct-url'), icon: 'content_paste', style: { 'font-size': '16px', diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html index 35b4fcba08..bde274c28f 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html @@ -41,10 +41,18 @@ ngxClipboard (cbOnSuccess)="onPackageChecksumCopied()" [cbContent]="entity?.checksum" - [fxShow]="!isEdit"> + [fxShow]="!isEdit && entity?.checksum"> ota-update.copy-checksum +
@@ -137,12 +145,12 @@
- ota-update.url + ota-update.direct-url - ota-update.url-required + ota-update.direct-url-required
diff --git a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts index 9193b37c8e..1182e6c40a 100644 --- a/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts +++ b/ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts @@ -160,6 +160,17 @@ export class OtaUpdateComponent extends EntityComponent implements O })); } + onPackageDirectUrlCopied() { + this.store.dispatch(new ActionNotificationShow( + { + message: this.translate.instant('ota-update.checksum-copied-message'), + type: 'success', + duration: 750, + verticalPosition: 'bottom', + horizontalPosition: 'right' + })); + } + prepareFormValue(formValue: any): any { delete formValue.resource; delete formValue.generateChecksum; diff --git a/ui-ngx/src/app/shared/components/button/copy-button.component.html b/ui-ngx/src/app/shared/components/button/copy-button.component.html index 6b38870d3e..20dd7d06ed 100644 --- a/ui-ngx/src/app/shared/components/button/copy-button.component.html +++ b/ui-ngx/src/app/shared/components/button/copy-button.component.html @@ -16,12 +16,9 @@ --> \n \n \n \n
\n
\n \n \n
\n
\n \n \n
\n", - "customCss": "", + "customCss": "form {\n min-width: 300px !important;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" }, { - "name": "Download firware", + "name": "Download firmware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { - "name": "Copy checksum", + "name": "Copy checksum/URL", "icon": "content_copy", "type": "custom", - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" } ] @@ -997,6 +1018,27 @@ "funcBody": null, "usePostProcessing": null, "postFuncBody": null + }, + { + "name": "fw_url", + "type": "attribute", + "label": "fw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.4204673738685043, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null } ] } @@ -1023,23 +1065,23 @@ "icon": "edit", "type": "customPretty", "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - "customCss": "", + "customCss": "form {\n min-width: 300px !important;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" }, { - "name": "Download firware", + "name": "Download firmware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { - "name": "Copy checksum", + "name": "Copy checksum/URL", "icon": "content_copy", "type": "custom", - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" } ] @@ -1273,6 +1315,27 @@ "funcBody": null, "usePostProcessing": null, "postFuncBody": null + }, + { + "name": "fw_url", + "type": "attribute", + "label": "fw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.4204673738685043, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null } ] } @@ -1299,23 +1362,23 @@ "icon": "edit", "type": "customPretty", "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - "customCss": "", + "customCss": "form {\n min-width: 300px !important;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" }, { - "name": "Download firware", + "name": "Download firmware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { - "name": "Copy checksum", + "name": "Copy checksum/URL", "icon": "content_copy", "type": "custom", - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" } ] @@ -1549,6 +1612,27 @@ "funcBody": null, "usePostProcessing": null, "postFuncBody": null + }, + { + "name": "fw_url", + "type": "attribute", + "label": "fw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.4204673738685043, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null } ] } @@ -1575,23 +1659,23 @@ "icon": "edit", "type": "customPretty", "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - "customCss": "", + "customCss": "form {\n min-width: 300px !important;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" }, { - "name": "Download firware", + "name": "Download firmware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { - "name": "Copy checksum", + "name": "Copy checksum/URL", "icon": "content_copy", "type": "custom", - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" } ] @@ -1825,6 +1909,27 @@ "funcBody": null, "usePostProcessing": null, "postFuncBody": null + }, + { + "name": "fw_url", + "type": "attribute", + "label": "fw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.4204673738685043, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null } ] } @@ -1851,23 +1956,23 @@ "icon": "edit", "type": "customPretty", "customHtml": "
\n \n

Edit firmware {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", - "customCss": "", + "customCss": "form {\n min-width: 300px !important;\n}", "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n firmwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n firmwareId: vm.entity.firmwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.firmwareId = formValues.firmwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", "customResources": [], "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" }, { - "name": "Download firware", + "name": "Download firmware", "icon": "file_download", "type": "custom", - "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n\n }\n });\n }\n }\n );\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceFirmware();\n\nfunction getDeviceFirmware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(data.firmwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.firmwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.firmwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "12533058-42f6-e75f-620c-219c48d01ec0" }, { - "name": "Copy checksum", + "name": "Copy checksum/URL", "icon": "content_copy", "type": "custom", - "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n}", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_checksum');\nvar checksum = data.data[0][1];\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Firmware checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'fw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Firmware direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not firmware set.', 2000, 'top');\n }\n}", "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" } ] @@ -1936,7 +2041,7 @@ } }, "device_firmware_history": { - "name": "Device Firmware history", + "name": "Firmware history: ${entityName}", "root": false, "layouts": { "main": { @@ -2379,7 +2484,8 @@ "titleColor": "rgba(0,0,0,0.870588)", "showFilters": true, "showDashboardLogo": false, - "dashboardLogoUrl": null + "dashboardLogoUrl": null, + "showUpdateDashboardImage": false } }, "name": "Firmware" diff --git a/application/src/main/data/json/demo/dashboards/software.json b/application/src/main/data/json/demo/dashboards/software.json new file mode 100644 index 0000000000..84316fa5fd --- /dev/null +++ b/application/src/main/data/json/demo/dashboards/software.json @@ -0,0 +1,2492 @@ +{ + "title": "Software", + "image": null, + "configuration": { + "description": "", + "widgets": { + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "entities_table", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "realtime": { + "interval": 1000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSearch": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityLabel", + "displayEntityName": false, + "displayEntityType": false, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": false, + "entitiesTitle": "Devices", + "displayEntityLabel": true, + "entityLabelColumnTitle": "Device" + }, + "title": "New Entities table", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current SW title", + "color": "#2196f3", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.09545533885166413, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current SW version", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7206056602328659, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target SW title", + "color": "#ffc107", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.9934225682766313, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target SW version", + "color": "#607d8b", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5251724416842531, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_ts", + "type": "timeseries", + "label": "Target SW set time", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" + }, + "_hash": 0.31823244858578237, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Progress", + "color": "#9c27b0", + "settings": { + "columnWidth": "30%", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `
`;\n}" + }, + "_hash": 0.8174211757846257, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "130px", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';" + }, + "_hash": 0.7764426948615217, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_checksum", + "type": "attribute", + "label": "sw_checksum", + "color": "#3f51b5", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.5594087842471693, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_url", + "type": "attribute", + "label": "sw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.3355829384124256, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "actions": { + "actionCellButton": [ + { + "name": "History software update", + "icon": "history", + "type": "openDashboardState", + "targetDashboardStateId": "device_software_history", + "setEntityId": true, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" + }, + { + "name": "Edit software", + "icon": "edit", + "type": "customPretty", + "customHtml": "
\n \n

Edit software {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customCss": "form {\n min-width: 300px !important;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", + "customResources": [], + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" + }, + { + "name": "Download software", + "icon": "file_download", + "type": "custom", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "12533058-42f6-e75f-620c-219c48d01ec0" + }, + { + "name": "Copy checksum/URL", + "icon": "content_copy", + "type": "custom", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {} + }, + "row": 0, + "col": 0, + "id": "cd03188e-cd9d-9601-fd57-da4cb95fc016" + }, + "100b756c-0082-6505-3ae1-3603e6deea48": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "timeseries_table", + "type": "timeseries", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 8, + "sizeY": 6.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "19f41c21-d9af-e666-8f50-e1748778f955", + "filterId": null, + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current software title", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.5978079905579401, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current software version", + "color": "#4caf50", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.027392025058568192, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target software title", + "color": "#f44336", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.9496350796287059, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target software version", + "color": "#ffc107", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.6734152252264187, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#607d8b", + "settings": { + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "" + }, + "_hash": 0.2983399718643074, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "function capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\nif (value !== '') {\n return capitalize(value);\n}\nreturn value;" + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 2592000000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "showTimestamp": true, + "displayPagination": true, + "defaultPageSize": 10, + "enableSearch": true, + "enableStickyHeader": true, + "enableStickyAction": true + }, + "title": "Software history", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "displayTimewindow": true, + "titleTooltip": "" + }, + "row": 0, + "col": 0, + "id": "100b756c-0082-6505-3ae1-3603e6deea48" + }, + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "html_value_card", + "type": "latest", + "title": "New widget", + "sizeX": 8, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": "", + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "waitingDevicesNumber", + "color": "#4caf50", + "settings": {}, + "_hash": 0.7404827038869322, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "cardHtml": "
\n
\n \n
\n ${waitingDevicesNumber:0}\n
\n
\n Device Waiting\n
\n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" + }, + "title": "New HTML Value Card", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "activeDevices", + "icon": "more_horiz", + "type": "openDashboardState", + "targetDashboardStateId": "device_waiting", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "4d9a77a2-f0a5-690c-a83b-b0e940be788c" + } + ] + }, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "enableDataExport": false, + "displayTimewindow": true + }, + "id": "17543c57-af4a-2c1e-bf12-53a7b46791e6" + }, + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "html_value_card", + "type": "latest", + "title": "New widget", + "sizeX": 8, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": "", + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "updatingDevicesNumber", + "color": "#4caf50", + "settings": {}, + "_hash": 0.7404827038869322, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "cardHtml": "
\n
\n \n
\n ${updatingDevicesNumber:0}\n
\n
\n Device Updating\n
\n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" + }, + "title": "New HTML Value Card", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "activeDevices", + "icon": "more_horiz", + "type": "openDashboardState", + "targetDashboardStateId": "device_updating", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "57d39904-2350-b29b-78ed-56b8268814cb" + } + ] + }, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "enableDataExport": false, + "displayTimewindow": true + }, + "id": "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6" + }, + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "html_value_card", + "type": "latest", + "title": "New widget", + "sizeX": 8, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": "", + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "6044e198-df64-cd76-f339-696f220c4943", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "updatedDevicesNumber", + "color": "#4caf50", + "settings": {}, + "_hash": 0.7404827038869322, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "cardHtml": "
\n
\n \n
\n ${updatedDevicesNumber:0}\n
\n
\n Device Updated\n
\n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .content img {\n height: 28px; \n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" + }, + "title": "New HTML Value Card", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "activeDevices", + "icon": "more_horiz", + "type": "openDashboardState", + "targetDashboardStateId": "device_updated", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "d787c212-8c56-34f0-349a-5aae2ffd1eae" + } + ] + }, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "enableDataExport": false, + "displayTimewindow": true + }, + "id": "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81" + }, + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "html_value_card", + "type": "latest", + "title": "New widget", + "sizeX": 8, + "sizeY": 3, + "config": { + "datasources": [ + { + "type": "entityCount", + "name": "", + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", + "dataKeys": [ + { + "name": "count", + "type": "count", + "label": "updatingDevicesNumber", + "color": "#4caf50", + "settings": {}, + "_hash": 0.7404827038869322, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "realtime": { + "timewindowMs": 60000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "cardHtml": "
\n
\n
\n \n \n \n
\n
\n ${updatingDevicesNumber:0}\n
\n \n
\n Device Failed\n
\n
\n
", + "cardCss": ".card {\n width: 100%;\n height: 100%;\n border: 1px solid #E0E0E0;\n box-sizing: border-box;\n}\n\n.card .content {\n padding: 20px 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n box-sizing: border-box;\n}\n\n.card .container-svg {\n height: 40px;\n width: 40px;\n}\n\n.card .value {\n margin: 18px 0 5px;\n font-weight: 500;\n font-size: 3em;\n line-height: 1.1em;\n text-align: center;\n letter-spacing: -0.02em;\n color: #333333;\n}\n\n.card .description {\n font-size: 1em;\n line-height: 1.1em;\n color: #000000;\n opacity: 0.6;\n text-align: center;\n letter-spacing: -0.02em;\n}\n\n@media (min-width: 960px) and (max-width: 1200px) {\n .card .container-svg {\n height: 28px;\n width: 28px;\n }\n \n .card .value {\n margin: 12px 0 5px;\n font-size: 2em;\n line-height: 1;\n }\n \n .card .description {\n font-size: 0.8em;\n line-height: 1;\n }\n}" + }, + "title": "New HTML Value Card", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": true, + "showLegend": false, + "actions": { + "elementClick": [ + { + "name": "activeDevices", + "icon": "more_horiz", + "type": "openDashboardState", + "targetDashboardStateId": "device_error", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "0b3d2887-9929-84d5-3795-0763dca15cba" + } + ] + }, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "enableDataExport": false, + "displayTimewindow": true + }, + "id": "77b10144-b904-edd5-8c7c-8fb75616c6d8" + }, + "21be08bb-ec90-f760-ad6f-e7678f12c401": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "entities_table", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "realtime": { + "interval": 1000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSearch": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityLabel", + "displayEntityName": false, + "displayEntityType": false, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": true, + "entitiesTitle": "Devices", + "displayEntityLabel": true, + "entityLabelColumnTitle": "Device" + }, + "title": "New Entities table", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current SW title", + "color": "#2196f3", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.09545533885166413, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current SW version", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7206056602328659, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target SW title", + "color": "#ffc107", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.9934225682766313, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target SW version", + "color": "#607d8b", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5251724416842531, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_ts", + "type": "timeseries", + "label": "Target SW set time", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" + }, + "_hash": 0.31823244858578237, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Progress", + "color": "#9c27b0", + "settings": { + "columnWidth": "30%", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `
`;\n}" + }, + "_hash": 0.8174211757846257, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "130px", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';" + }, + "_hash": 0.7764426948615217, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_checksum", + "type": "attribute", + "label": "sw_checksum", + "color": "#3f51b5", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.5594087842471693, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_url", + "type": "attribute", + "label": "sw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.3355829384124256, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "actions": { + "actionCellButton": [ + { + "name": "History software update", + "icon": "history", + "type": "openDashboardState", + "targetDashboardStateId": "device_software_history", + "setEntityId": true, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" + }, + { + "name": "Edit software", + "icon": "edit", + "type": "customPretty", + "customHtml": "
\n \n

Edit software {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customCss": "form {\n min-width: 300px !important;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", + "customResources": [], + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" + }, + { + "name": "Download software", + "icon": "file_download", + "type": "custom", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "12533058-42f6-e75f-620c-219c48d01ec0" + }, + { + "name": "Copy checksum/URL", + "icon": "content_copy", + "type": "custom", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {} + }, + "row": 0, + "col": 0, + "id": "21be08bb-ec90-f760-ad6f-e7678f12c401" + }, + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "entities_table", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "realtime": { + "interval": 1000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSearch": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityLabel", + "displayEntityName": false, + "displayEntityType": false, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": true, + "entitiesTitle": "Devices", + "displayEntityLabel": true, + "entityLabelColumnTitle": "Device" + }, + "title": "New Entities table", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "579f0468-9ce9-7e3e-b34c-88dd3de59897", + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current SW title", + "color": "#2196f3", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.09545533885166413, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current SW version", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7206056602328659, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target SW title", + "color": "#ffc107", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.9934225682766313, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target SW version", + "color": "#607d8b", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5251724416842531, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_ts", + "type": "timeseries", + "label": "Target SW set time", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" + }, + "_hash": 0.31823244858578237, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Progress", + "color": "#9c27b0", + "settings": { + "columnWidth": "30%", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `
`;\n}" + }, + "_hash": 0.8174211757846257, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "130px", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';" + }, + "_hash": 0.7764426948615217, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_checksum", + "type": "attribute", + "label": "sw_checksum", + "color": "#3f51b5", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.5594087842471693, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_url", + "type": "attribute", + "label": "sw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.3355829384124256, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "actions": { + "actionCellButton": [ + { + "name": "History software update", + "icon": "history", + "type": "openDashboardState", + "targetDashboardStateId": "device_software_history", + "setEntityId": true, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" + }, + { + "name": "Edit software", + "icon": "edit", + "type": "customPretty", + "customHtml": "
\n \n

Edit software {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customCss": "form {\n min-width: 300px !important;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", + "customResources": [], + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" + }, + { + "name": "Download software", + "icon": "file_download", + "type": "custom", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "12533058-42f6-e75f-620c-219c48d01ec0" + }, + { + "name": "Copy checksum/URL", + "icon": "content_copy", + "type": "custom", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {} + }, + "row": 0, + "col": 0, + "id": "e8280043-d3dc-7acb-c2ff-a4522972ff91" + }, + "3624013b-378c-f110-5eba-ae95c25a4dcc": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "entities_table", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "realtime": { + "interval": 1000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSearch": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityLabel", + "displayEntityName": false, + "displayEntityType": false, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": true, + "entitiesTitle": "Devices", + "displayEntityLabel": true, + "entityLabelColumnTitle": "Device" + }, + "title": "New Entities table", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current SW title", + "color": "#2196f3", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.09545533885166413, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current SW version", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7206056602328659, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target SW title", + "color": "#ffc107", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.9934225682766313, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target SW version", + "color": "#607d8b", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5251724416842531, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_ts", + "type": "timeseries", + "label": "Target SW set time", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" + }, + "_hash": 0.31823244858578237, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Progress", + "color": "#9c27b0", + "settings": { + "columnWidth": "30%", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `
`;\n}" + }, + "_hash": 0.8174211757846257, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "130px", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';" + }, + "_hash": 0.7764426948615217, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_checksum", + "type": "attribute", + "label": "sw_checksum", + "color": "#3f51b5", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.5594087842471693, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_url", + "type": "attribute", + "label": "sw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.3355829384124256, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "actions": { + "actionCellButton": [ + { + "name": "History software update", + "icon": "history", + "type": "openDashboardState", + "targetDashboardStateId": "device_software_history", + "setEntityId": true, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" + }, + { + "name": "Edit software", + "icon": "edit", + "type": "customPretty", + "customHtml": "
\n \n

Edit software {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customCss": "form {\n min-width: 300px !important;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", + "customResources": [], + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" + }, + { + "name": "Download software", + "icon": "file_download", + "type": "custom", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "12533058-42f6-e75f-620c-219c48d01ec0" + }, + { + "name": "Copy checksum/URL", + "icon": "content_copy", + "type": "custom", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {} + }, + "row": 0, + "col": 0, + "id": "3624013b-378c-f110-5eba-ae95c25a4dcc" + }, + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { + "isSystemType": true, + "bundleAlias": "cards", + "typeAlias": "entities_table", + "type": "latest", + "title": "New widget", + "image": null, + "description": null, + "sizeX": 7.5, + "sizeY": 6.5, + "config": { + "timewindow": { + "realtime": { + "interval": 1000, + "timewindowMs": 86400000 + }, + "aggregation": { + "type": "NONE", + "limit": 200 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "4px", + "settings": { + "enableSearch": true, + "displayPagination": true, + "defaultPageSize": 10, + "defaultSortOrder": "entityLabel", + "displayEntityName": false, + "displayEntityType": false, + "enableSelectColumnDisplay": false, + "enableStickyHeader": true, + "enableStickyAction": true, + "entitiesTitle": "Devices", + "displayEntityLabel": true, + "entityLabelColumnTitle": "Device" + }, + "title": "New Entities table", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400, + "padding": "5px 10px 5px 10px" + }, + "useDashboardTimewindow": false, + "showLegend": false, + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "filterId": "6044e198-df64-cd76-f339-696f220c4943", + "dataKeys": [ + { + "name": "current_sw_title", + "type": "timeseries", + "label": "Current SW title", + "color": "#2196f3", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.09545533885166413, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "current_sw_version", + "type": "timeseries", + "label": "Current SW version", + "color": "#4caf50", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.7206056602328659, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_title", + "type": "timeseries", + "label": "Target SW title", + "color": "#ffc107", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.9934225682766313, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_version", + "type": "timeseries", + "label": "Target SW version", + "color": "#607d8b", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled" + }, + "_hash": 0.5251724416842531, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "target_sw_ts", + "type": "timeseries", + "label": "Target SW set time", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellContentFunction": "if (value !== '') {\n return ctx.date.transform(value, 'yyyy-MM-dd HH:mm:ss');\n}\nreturn '';" + }, + "_hash": 0.31823244858578237, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Progress", + "color": "#9c27b0", + "settings": { + "columnWidth": "30%", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "return {\n 'padding-right': '30px'\n}", + "cellContentFunction": "if (value !== '') {\n var mapProgress = {\n 'QUEUED': 0,\n 'INITIATED': 5,\n 'DOWNLOADING': 10,\n 'DOWNLOADED': 55,\n 'VERIFIED': 60,\n 'UPDATING': 70,\n 'FAILED': 99,\n 'UPDATED': 100\n }\n var color = 'mat-primary';\n var progress = mapProgress[value];\n if (value == 'FAILED') {\n color = 'mat-accent';\n }\n return `
`;\n}" + }, + "_hash": 0.8174211757846257, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_state", + "type": "timeseries", + "label": "Status", + "color": "#f44336", + "settings": { + "columnWidth": "130px", + "useCellStyleFunction": true, + "useCellContentFunction": true, + "defaultColumnVisibility": "visible", + "columnSelectionToDisplay": "enabled", + "cellStyleFunction": "if (value == 'FAILED') {\n return {'color' : '#D93025'};\n}\nreturn {};", + "cellContentFunction": "function icon(value) {\n if (value == 'QUEUED') {\n return '';\n }\n if (value == 'INITIATED' || value == 'DOWNLOADING' || value == 'DOWNLOADED') {\n return '';\n }\n if (value == 'VERIFIED' || value == 'UPDATING' ) {\n return 'update';\n }\n if (value == 'UPDATED') {\n return '';\n }\n if (value == 'FAILED') {\n return 'warning';\n }\n return '';\n}\nfunction capitalize (s) {\n if (typeof s !== 'string') return '';\n return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();\n}\n\nreturn icon(value) + '' + capitalize(value) + '';" + }, + "_hash": 0.7764426948615217, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_checksum", + "type": "attribute", + "label": "sw_checksum", + "color": "#3f51b5", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.5594087842471693, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "sw_url", + "type": "attribute", + "label": "sw_url", + "color": "#e91e63", + "settings": { + "columnWidth": "0px", + "useCellStyleFunction": false, + "cellStyleFunction": "", + "useCellContentFunction": false, + "cellContentFunction": "", + "defaultColumnVisibility": "hidden", + "columnSelectionToDisplay": "disabled" + }, + "_hash": 0.3355829384124256, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "actions": { + "actionCellButton": [ + { + "name": "History software update", + "icon": "history", + "type": "openDashboardState", + "targetDashboardStateId": "device_software_history", + "setEntityId": true, + "stateEntityParamName": null, + "openInSeparateDialog": false, + "dialogTitle": "", + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "98a1406c-3301-bc2f-2c5d-d637ce3b663b" + }, + { + "name": "Edit software", + "icon": "edit", + "type": "customPretty", + "customHtml": "
\n \n

Edit software {{entityName}}

\n \n \n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
", + "customCss": "form {\n min-width: 300px !important;\n}", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet customDialog = $injector.get(widgetContext.servicesMap.get('customDialog'));\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet deviceService = $injector.get(widgetContext.servicesMap.get('deviceService'));\n\nopenEditEntityDialog();\n\nfunction openEditEntityDialog() {\n customDialog.customDialog(htmlTemplate, EditEntityDialogController).subscribe();\n}\n\nfunction EditEntityDialogController(instance) {\n let vm = instance;\n\n vm.entityName = entityName;\n vm.entity = {};\n\n vm.editEntityFormGroup = vm.fb.group({\n softwareId: [null]\n });\n\n getEntityInfo();\n\n vm.cancel = function() {\n vm.dialogRef.close(null);\n };\n\n vm.save = function() {\n vm.editEntityFormGroup.markAsPristine();\n saveEntity().subscribe(\n function () {\n // widgetContext.updateAliases();\n vm.dialogRef.close(null);\n }\n );\n };\n\n\n function getEntityInfo() {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n vm.entity = data;\n vm.editEntityFormGroup.patchValue({\n softwareId: vm.entity.softwareId\n }, {emitEvent: false});\n }\n );\n }\n\n function saveEntity() {\n const formValues = vm.editEntityFormGroup.value;\n vm.entity.softwareId = formValues.softwareId;\n return deviceService.saveDevice(vm.entity);\n }\n}", + "customResources": [], + "id": "23099c1d-454b-25dc-8bc0-7cf33c21c5d5" + }, + { + "name": "Download software", + "icon": "file_download", + "type": "custom", + "customFunction": "let $injector = widgetContext.$scope.$injector;\nlet entityService = $injector.get(widgetContext.servicesMap.get('entityService'));\nlet otaPackageService = $injector.get(widgetContext.servicesMap.get('otaPackageService'));\nlet deviceProfileService = $injector.get(widgetContext.servicesMap.get('deviceProfileService'));\n\ngetDeviceSoftware();\n\nfunction getDeviceSoftware() {\n var entityIdValue = entityId.id;\n var data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url === '') {\n entityService.getEntity(entityId.entityType, entityId.id).subscribe(\n function (data) {\n if (data.softwareId !== null) {\n otaPackageService.downloadOtaPackage(data.softwareId.id).subscribe(); \n } else {\n deviceProfileService.getDeviceProfile(data.deviceProfileId.id).subscribe(\n function (deviceProfile) {\n if (deviceProfile.softwareId !== null) {\n otaPackageService.downloadOtaPackage(deviceProfile.softwareId.id).subscribe();\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n });\n }\n }\n );\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "12533058-42f6-e75f-620c-219c48d01ec0" + }, + { + "name": "Copy checksum/URL", + "icon": "content_copy", + "type": "custom", + "customFunction": "function copyToClipboard(text) {\n if (window.clipboardData && window.clipboardData.setData) {\n return window.clipboardData.setData(\"Text\", text);\n\n }\n else if (document.queryCommandSupported && document.queryCommandSupported(\"copy\")) {\n var textarea = document.createElement(\"textarea\");\n textarea.textContent = text;\n textarea.style.position = \"fixed\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n return document.execCommand(\"copy\");\n }\n catch (ex) {\n console.warn(\"Copy to clipboard failed.\", ex);\n return false;\n }\n document.body.removeChild(textarea);\n }\n}\nvar entityIdValue = entityId.id;\nvar data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_checksum');\nvar checksum = data.data[0][1];\nconsole.log(checksum);\nif (checksum !== '') {\n copyToClipboard(checksum);\n widgetContext.showSuccessToast('Software checksum has been copied to clipboard', 2000, 'top');\n} else {\n data = widgetContext.data.find((el) => el.datasource.entityId === entityIdValue && el.dataKey.name === 'sw_url');\n var url = data.data[0][1];\n if (url !== '') {\n copyToClipboard(url);\n widgetContext.showSuccessToast('Software direct URL has been copied to clipboard', 2000, 'top');\n } else {\n widgetContext.showToast('warn', 'Device ' + entityName +' has not software set.', 2000, 'top');\n }\n}", + "id": "09323079-7111-87f7-90d1-c62cd7d85dc7" + } + ] + }, + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "widgetStyle": {} + }, + "row": 0, + "col": 0, + "id": "d2d13e0d-4e71-889f-9343-ad2f0af9f176" + } + }, + "states": { + "default": { + "name": "Device list", + "root": true, + "layouts": { + "main": { + "widgets": { + "cd03188e-cd9d-9601-fd57-da4cb95fc016": { + "sizeX": 19, + "sizeY": 12, + "row": 0, + "col": 0 + }, + "17543c57-af4a-2c1e-bf12-53a7b46791e6": { + "sizeX": 5, + "sizeY": 3, + "row": 0, + "col": 19 + }, + "6c1c4e1a-bce0-f5ad-ff8b-ba1dfc5a4ec6": { + "sizeX": 5, + "sizeY": 3, + "row": 3, + "col": 19 + }, + "e6674227-9cf3-a2f6-ecac-5ccfc38a3c81": { + "sizeX": 5, + "sizeY": 3, + "row": 9, + "col": 19 + }, + "77b10144-b904-edd5-8c7c-8fb75616c6d8": { + "sizeX": 5, + "sizeY": 3, + "row": 6, + "col": 19 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 12, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": true, + "mobileRowHeight": 70 + } + } + } + }, + "device_software_history": { + "name": "Software history: ${entityName}", + "root": false, + "layouts": { + "main": { + "widgets": { + "100b756c-0082-6505-3ae1-3603e6deea48": { + "sizeX": 24, + "sizeY": 12, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "device_waiting": { + "name": "Device waiting", + "root": false, + "layouts": { + "main": { + "widgets": { + "21be08bb-ec90-f760-ad6f-e7678f12c401": { + "sizeX": 24, + "sizeY": 12, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "device_updating": { + "name": "Device updating", + "root": false, + "layouts": { + "main": { + "widgets": { + "e8280043-d3dc-7acb-c2ff-a4522972ff91": { + "sizeX": 24, + "sizeY": 12, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "device_updated": { + "name": "Device updated", + "root": false, + "layouts": { + "main": { + "widgets": { + "d2d13e0d-4e71-889f-9343-ad2f0af9f176": { + "sizeX": 27, + "sizeY": 12, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + }, + "device_error": { + "name": "Device failed", + "root": false, + "layouts": { + "main": { + "widgets": { + "3624013b-378c-f110-5eba-ae95c25a4dcc": { + "sizeX": 24, + "sizeY": 12, + "row": 0, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 10, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70 + } + } + } + } + }, + "entityAliases": { + "639da5b4-31f0-0151-6282-c37a3897b7e8": { + "id": "639da5b4-31f0-0151-6282-c37a3897b7e8", + "alias": "All devices", + "filter": { + "type": "entityType", + "resolveMultiple": true, + "entityType": "DEVICE" + } + }, + "19f41c21-d9af-e666-8f50-e1748778f955": { + "id": "19f41c21-d9af-e666-8f50-e1748778f955", + "alias": "State entity", + "filter": { + "type": "stateEntity", + "resolveMultiple": false, + "stateEntityParamName": null, + "defaultStateEntity": null + } + } + }, + "filters": { + "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e": { + "id": "19a0ad1c-b31d-4a29-9d7b-5d87e2a8ea6e", + "filter": "WaitingDevicesFilter", + "keyFilters": [ + { + "key": { + "type": "TIME_SERIES", + "key": "sw_state" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "QUEUED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "579f0468-9ce9-7e3e-b34c-88dd3de59897": { + "id": "579f0468-9ce9-7e3e-b34c-88dd3de59897", + "filter": "UpdatingDevicesFilter", + "keyFilters": [ + { + "key": { + "type": "TIME_SERIES", + "key": "sw_state" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "OR", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "INITIATED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": false, + "label": "sw_state equel", + "autogeneratedLabel": true, + "order": 0 + } + }, + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "DOWNLOADING", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": false, + "label": "sw_state equal", + "autogeneratedLabel": true, + "order": 0 + } + }, + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "DOWNLOADED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": false, + "label": "sw_state equal", + "autogeneratedLabel": true, + "order": 0 + } + }, + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "VERIFIED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": false, + "label": "sw_state equal", + "autogeneratedLabel": true, + "order": 0 + } + }, + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "UPDATING", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": false, + "label": "sw_state equal", + "autogeneratedLabel": true, + "order": 0 + } + } + ], + "type": "COMPLEX" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "6044e198-df64-cd76-f339-696f220c4943": { + "id": "6044e198-df64-cd76-f339-696f220c4943", + "filter": "UpdetedDevicesFilter", + "keyFilters": [ + { + "key": { + "type": "TIME_SERIES", + "key": "sw_state" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "UPDATED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "bdbc6ea1-95a7-3912-341a-58dc7704a00f": { + "id": "bdbc6ea1-95a7-3912-341a-58dc7704a00f", + "filter": "FailedDevicesFilter", + "keyFilters": [ + { + "key": { + "type": "TIME_SERIES", + "key": "sw_state" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "EQUAL", + "value": { + "defaultValue": "FAILED", + "dynamicValue": null + }, + "ignoreCase": false, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "", + "autogeneratedLabel": true, + "order": 0 + } + } + ] + } + ], + "editable": false + }, + "8fdb88d0-50ac-2232-fdb7-69c30c16544e": { + "id": "8fdb88d0-50ac-2232-fdb7-69c30c16544e", + "filter": "DeviceSearch", + "keyFilters": [ + { + "key": { + "type": "ENTITY_FIELD", + "key": "name" + }, + "valueType": "STRING", + "predicates": [ + { + "keyFilterPredicate": { + "operation": "CONTAINS", + "value": { + "defaultValue": "" + }, + "ignoreCase": true, + "type": "STRING" + }, + "userInfo": { + "editable": true, + "label": "Device name", + "autogeneratedLabel": false, + "order": 0 + } + } + ] + } + ], + "editable": true + } + }, + "timewindow": { + "displayValue": "", + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1618998609030, + "endTimeMs": 1619085009030 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": false, + "showEntitiesSelect": false, + "showDashboardTimewindow": true, + "showDashboardExport": false, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showFilters": true, + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "showUpdateDashboardImage": false + } + }, + "name": "Software" +} \ No newline at end of file From 8e4f8594f74a60889ec394556a452217476a6980 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 10 Jun 2021 11:50:11 +0300 Subject: [PATCH 73/75] Refactor OAuth2 settings. Add mobile application parameters: package and callback URL scheme --- .../main/data/upgrade/3.2.2/schema_update.sql | 61 +++ ...tomOAuth2AuthorizationRequestResolver.java | 30 +- .../server/controller/OAuth2Controller.java | 18 +- .../update/DefaultDataUpdateService.java | 26 +- .../oauth2/AbstractOAuth2ClientMapper.java | 12 +- .../auth/oauth2/BasicOAuth2ClientMapper.java | 8 +- .../auth/oauth2/CustomOAuth2ClientMapper.java | 8 +- .../auth/oauth2/GithubOAuth2ClientMapper.java | 8 +- .../auth/oauth2/OAuth2ClientMapper.java | 5 +- .../Oauth2AuthenticationFailureHandler.java | 16 +- .../Oauth2AuthenticationSuccessHandler.java | 27 +- .../auth/oauth2/TbOAuth2ParameterNames.java | 22 + .../server/dao/oauth2/OAuth2Service.java | 20 +- .../server/common/data/id/OAuth2DomainId.java | 33 ++ .../server/common/data/id/OAuth2MobileId.java | 33 ++ .../server/common/data/id/OAuth2ParamsId.java | 33 ++ .../common/data/id/OAuth2RegistrationId.java | 33 ++ .../OAuth2ClientRegistrationId.java | 4 +- .../OAuth2ClientRegistrationInfoId.java | 4 +- .../OAuth2ClientRegistrationTemplate.java | 2 - .../common/data/oauth2/OAuth2Domain.java | 42 ++ .../common/data/oauth2/OAuth2DomainInfo.java | 34 ++ .../server/common/data/oauth2/OAuth2Info.java | 31 ++ .../common/data/oauth2/OAuth2Mobile.java | 42 ++ .../common/data/oauth2/OAuth2MobileInfo.java | 34 ++ .../common/data/oauth2/OAuth2Params.java | 40 ++ .../common/data/oauth2/OAuth2ParamsInfo.java | 39 ++ .../data/oauth2/OAuth2Registration.java | 77 +++ ...onDto.java => OAuth2RegistrationInfo.java} | 3 +- .../deprecated/ClientRegistrationDto.java | 45 ++ .../oauth2/{ => deprecated}/DomainInfo.java | 4 +- .../ExtendedOAuth2ClientRegistrationInfo.java | 5 +- .../OAuth2ClientRegistration.java | 8 +- .../OAuth2ClientRegistrationInfo.java | 6 +- .../OAuth2ClientsDomainParams.java | 4 +- .../{ => deprecated}/OAuth2ClientsParams.java | 4 +- .../server/dao/model/ModelConstants.java | 15 +- .../dao/model/sql/OAuth2DomainEntity.java | 76 +++ .../dao/model/sql/OAuth2MobileEntity.java | 72 +++ .../dao/model/sql/OAuth2ParamsEntity.java | 65 +++ .../model/sql/OAuth2RegistrationEntity.java | 212 ++++++++ ...actOAuth2ClientRegistrationInfoEntity.java | 7 +- ...dedOAuth2ClientRegistrationInfoEntity.java | 5 +- .../OAuth2ClientRegistrationEntity.java | 9 +- .../OAuth2ClientRegistrationInfoEntity.java | 5 +- .../HybridClientRegistrationRepository.java | 32 +- .../server/dao/oauth2/OAuth2DomainDao.java | 28 + .../server/dao/oauth2/OAuth2MobileDao.java | 28 + .../server/dao/oauth2/OAuth2ParamsDao.java | 23 + .../dao/oauth2/OAuth2RegistrationDao.java | 33 ++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 221 +++++++- .../server/dao/oauth2/OAuth2Utils.java | 144 +++++- .../OAuth2ClientRegistrationDao.java | 5 +- .../OAuth2ClientRegistrationInfoDao.java | 8 +- .../dao/sql/oauth2/JpaOAuth2DomainDao.java | 52 ++ .../dao/sql/oauth2/JpaOAuth2MobileDao.java | 52 ++ .../dao/sql/oauth2/JpaOAuth2ParamsDao.java | 47 ++ .../sql/oauth2/JpaOAuth2RegistrationDao.java | 62 +++ .../sql/oauth2/OAuth2DomainRepository.java | 29 ++ .../sql/oauth2/OAuth2MobileRepository.java | 28 + .../sql/oauth2/OAuth2ParamsRepository.java | 24 + .../oauth2/OAuth2RegistrationRepository.java | 52 ++ .../JpaOAuth2ClientRegistrationDao.java | 10 +- .../JpaOAuth2ClientRegistrationInfoDao.java | 11 +- ...Auth2ClientRegistrationInfoRepository.java | 9 +- .../OAuth2ClientRegistrationRepository.java | 5 +- .../resources/sql/schema-entities-hsql.sql | 75 ++- .../main/resources/sql/schema-entities.sql | 75 ++- .../dao/service/BaseOAuth2ServiceTest.java | 486 ++++++++++-------- .../resources/sql/hsql/drop-all-tables.sql | 6 +- .../resources/sql/psql/drop-all-tables.sql | 6 +- .../thingsboard/rest/client/RestClient.java | 24 +- ui-ngx/src/app/core/auth/auth.service.ts | 10 +- ui-ngx/src/app/core/http/oauth2.service.ts | 10 +- .../admin/oauth2-settings.component.html | 196 ++++--- .../admin/oauth2-settings.component.scss | 2 +- .../pages/admin/oauth2-settings.component.ts | 233 +++++---- ui-ngx/src/app/shared/models/oauth2.models.ts | 22 +- .../assets/locale/locale.constant-en_US.json | 11 +- 79 files changed, 2801 insertions(+), 545 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java rename common/data/src/main/java/org/thingsboard/server/common/data/id/{ => deprecated}/OAuth2ClientRegistrationId.java (89%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{ => deprecated}/OAuth2ClientRegistrationInfoId.java (89%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ClientRegistrationDto.java => OAuth2RegistrationInfo.java} (92%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/DomainInfo.java (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/ExtendedOAuth2ClientRegistrationInfo.java (85%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientRegistration.java (81%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfo.java (92%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientsDomainParams.java (92%) rename common/data/src/main/java/org/thingsboard/server/common/data/oauth2/{ => deprecated}/OAuth2ClientsParams.java (92%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/AbstractOAuth2ClientRegistrationInfoEntity.java (97%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/ExtendedOAuth2ClientRegistrationInfoEntity.java (91%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/OAuth2ClientRegistrationEntity.java (89%) rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{ => deprecated}/OAuth2ClientRegistrationInfoEntity.java (91%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java rename dao/src/main/java/org/thingsboard/server/dao/oauth2/{ => deprecated}/OAuth2ClientRegistrationDao.java (83%) rename dao/src/main/java/org/thingsboard/server/dao/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfoDao.java (81%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/JpaOAuth2ClientRegistrationDao.java (82%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/JpaOAuth2ClientRegistrationInfoDao.java (85%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/OAuth2ClientRegistrationInfoRepository.java (81%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/{ => deprecated}/OAuth2ClientRegistrationRepository.java (83%) diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 5647c301cf..9e94caf1af 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -79,6 +79,67 @@ CREATE TABLE IF NOT EXISTS ota_package ( CONSTRAINT ota_package_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) ); +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, + enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) +); + ALTER TABLE dashboard ADD COLUMN IF NOT EXISTS image varchar(1000000); diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java index bbcb7d656a..bc93253a73 100644 --- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java +++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java @@ -37,6 +37,8 @@ import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; import org.thingsboard.server.utils.MiscUtils; import javax.servlet.http.HttpServletRequest; @@ -46,12 +48,13 @@ import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.HashMap; import java.util.Map; +import java.util.UUID; @Service @Slf4j public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { - public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; - public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; + private static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; + private static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; private static final char PATH_DELIMITER = '/'; @@ -63,6 +66,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza @Autowired private ClientRegistrationRepository clientRegistrationRepository; + @Autowired + private OAuth2Service oAuth2Service; + @Autowired(required = false) private OAuth2Configuration oauth2Configuration; @@ -71,7 +77,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { String registrationId = this.resolveRegistrationId(request); String redirectUriAction = getAction(request, "login"); - return resolve(request, registrationId, redirectUriAction); + String appPackage = getAppPackage(request); + return resolve(request, registrationId, redirectUriAction, appPackage); } @Override @@ -80,7 +87,8 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza return null; } String redirectUriAction = getAction(request, "authorize"); - return resolve(request, registrationId, redirectUriAction); + String appPackage = getAppPackage(request); + return resolve(request, registrationId, redirectUriAction, appPackage); } private String getAction(HttpServletRequest request, String defaultAction) { @@ -91,8 +99,12 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza return action; } + private String getAppPackage(HttpServletRequest request) { + return request.getParameter("pkg"); + } + @SuppressWarnings("deprecation") - private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction, String appPackage) { if (registrationId == null) { return null; } @@ -104,6 +116,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza Map attributes = new HashMap<>(); attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); + if (!StringUtils.isEmpty(appPackage)) { + String callbackUrlScheme = this.oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), appPackage); + if (StringUtils.isEmpty(callbackUrlScheme)) { + throw new IllegalArgumentException("Invalid package: " + appPackage + ". No package info found for Client Registration."); + } else { + attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); + } + } OAuth2AuthorizationRequest.Builder builder; if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 6591a1577a..349c6de672 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -22,12 +22,13 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; @@ -49,7 +50,8 @@ public class OAuth2Controller extends BaseController { @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody - public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { + public List getOAuth2Clients(HttpServletRequest request, + @RequestParam(required = false) String pkgName) throws ThingsboardException { try { if (log.isDebugEnabled()) { log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort()); @@ -59,7 +61,7 @@ public class OAuth2Controller extends BaseController { log.debug("Header: {} {}", header, request.getHeader(header)); } } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request)); + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName); } catch (Exception e) { throw handleException(e); } @@ -68,10 +70,10 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody - public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { + public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return oAuth2Service.findOAuth2Params(); + return oAuth2Service.findOAuth2Info(); } catch (Exception e) { throw handleException(e); } @@ -80,11 +82,11 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { + public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { try { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); - oAuth2Service.saveOAuth2Params(oauth2Params); - return oAuth2Service.findOAuth2Params(); + oAuth2Service.saveOAuth2Info(oauth2Info); + return oAuth2Service.findOAuth2Info(); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java index b91c46417c..8c27417f90 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java @@ -23,6 +23,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.profile.TbDeviceProfileNode; import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration; import org.thingsboard.server.common.data.EntityView; @@ -35,6 +36,8 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.BaseReadTsKvQuery; import org.thingsboard.server.common.data.kv.ReadTsKvQuery; import org.thingsboard.server.common.data.kv.TsKvEntry; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.TimePageLink; @@ -45,10 +48,11 @@ import org.thingsboard.server.dao.alarm.AlarmDao; import org.thingsboard.server.dao.alarm.AlarmService; import org.thingsboard.server.dao.entity.EntityService; import org.thingsboard.server.dao.entityview.EntityViewService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2Utils; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; import org.thingsboard.server.dao.timeseries.TimeseriesService; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.service.install.InstallScripts; import java.util.ArrayList; @@ -88,6 +92,9 @@ public class DefaultDataUpdateService implements DataUpdateService { @Autowired private AlarmDao alarmDao; + @Autowired + private OAuth2Service oAuth2Service; + @Override public void updateData(String fromVersion) throws Exception { switch (fromVersion) { @@ -107,6 +114,7 @@ public class DefaultDataUpdateService implements DataUpdateService { log.info("Updating data from version 3.2.2 to 3.3.0 ..."); tenantsDefaultEdgeRuleChainUpdater.updateEntities(null); tenantsAlarmsCustomerUpdater.updateEntities(null); + updateOAuth2Params(); break; default: throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion); @@ -362,4 +370,20 @@ public class DefaultDataUpdateService implements DataUpdateService { } } + private void updateOAuth2Params() { + try { + OAuth2ClientsParams oauth2ClientsParams = oAuth2Service.findOAuth2Params(); + if (!oauth2ClientsParams.getDomainsParams().isEmpty()) { + log.info("Updating OAuth2 parameters ..."); + OAuth2Info oAuth2Info = OAuth2Utils.clientParamsToOAuth2Info(oauth2ClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + oAuth2Service.saveOAuth2Params(new OAuth2ClientsParams(false, Collections.emptyList())); + log.info("Successfully updated OAuth2 parameters!"); + } + } + catch (Exception e) { + log.error("Failed to update OAuth2 parameters", e); + } + } + } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index 4f38cf7a23..3ff3534806 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -33,8 +33,8 @@ import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.IdBased; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; @@ -93,9 +93,9 @@ public abstract class AbstractOAuth2ClientMapper { private final Lock userCreationLock = new ReentrantLock(); - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2Registration registration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + OAuth2MapperConfig config = registration.getMapperConfig(); UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); @@ -139,9 +139,9 @@ public abstract class AbstractOAuth2ClientMapper { } } - if (clientRegistration.getAdditionalInfo() != null && - clientRegistration.getAdditionalInfo().has("providerName")) { - additionalInfo.put("authProviderName", clientRegistration.getAdditionalInfo().get("providerName").asText()); + if (registration.getAdditionalInfo() != null && + registration.getAdditionalInfo().has("providerName")) { + additionalInfo.put("authProviderName", registration.getAdditionalInfo().get("providerName").asText()); } user.setAdditionalInfo(additionalInfo); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 940bf8ad0a..f5172b64e7 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -18,8 +18,8 @@ package org.thingsboard.server.service.security.auth.oauth2; import lombok.extern.slf4j.Slf4j; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -30,12 +30,12 @@ import java.util.Map; public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); Map attributes = token.getPrincipal().getAttributes(); String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index cb08bc9f96..65ebb384de 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -23,9 +23,9 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -39,10 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); - return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); } private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java index 8e41c4a747..d6260fe482 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java @@ -23,8 +23,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -46,13 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private OAuth2Configuration oAuth2Configuration; @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { - OAuth2MapperConfig config = clientRegistration.getMapperConfig(); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { + OAuth2MapperConfig config = registration.getMapperConfig(); Map githubMapperConfig = oAuth2Configuration.getGithubMapper(); String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); Map attributes = token.getPrincipal().getAttributes(); OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); - return getOrCreateSecurityUserFromOAuth2User(oAuth2User, clientRegistration); + return getOrCreateSecurityUserFromOAuth2User(oAuth2User, registration); } private synchronized String getEmail(String emailUrl, String oauth2Token) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index 965b34d8b0..280418d066 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -16,9 +16,10 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.service.security.model.SecurityUser; public interface OAuth2ClientMapper { - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration); + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java index b6345f1618..0e413b4f22 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationFailureHandler.java @@ -18,8 +18,10 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -51,9 +53,19 @@ public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationF public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + String baseUrl; + String errorPrefix; + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); + if (!StringUtils.isEmpty(callbackUrlScheme)) { + baseUrl = callbackUrlScheme + ":"; + errorPrefix = "/?error="; + } else { + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + errorPrefix = "/login?loginError="; + } httpCookieOAuth2AuthorizationRequestRepository.removeAuthorizationRequestCookies(request, response); - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 72a6c65f7c..227d733ebd 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -20,12 +20,14 @@ import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.security.model.JwtToken; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; @@ -72,17 +74,24 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - String baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + OAuth2AuthorizationRequest authorizationRequest = httpCookieOAuth2AuthorizationRequestRepository.loadAuthorizationRequest(request); + String callbackUrlScheme = authorizationRequest.getAttribute(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME); + String baseUrl; + if (!StringUtils.isEmpty(callbackUrlScheme)) { + baseUrl = callbackUrlScheme + ":"; + } else { + baseUrl = this.systemSecurityService.getBaseUrl(TenantId.SYS_TENANT_ID, new CustomerId(EntityId.NULL_UUID), request); + } try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId())); + OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId())); OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( token.getAuthorizedClientRegistrationId(), token.getPrincipal().getName()); - OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), - clientRegistration); + registration); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); @@ -91,7 +100,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS getRedirectStrategy().sendRedirect(request, response, baseUrl + "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); } catch (Exception e) { clearAuthenticationAttributes(request, response); - getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" + + String errorPrefix; + if (!StringUtils.isEmpty(callbackUrlScheme)) { + errorPrefix = "/?error="; + } else { + errorPrefix = "/login?loginError="; + } + getRedirectStrategy().sendRedirect(request, response, baseUrl + errorPrefix + URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java new file mode 100644 index 0000000000..aa5b32f055 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/TbOAuth2ParameterNames.java @@ -0,0 +1,22 @@ +/** + * Copyright © 2016-2021 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.service.security.auth.oauth2; + +public interface TbOAuth2ParameterNames { + + String CALLBACK_URL_SCHEME = "callback_url_scheme"; + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 3f9411d0aa..158c3911b4 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -16,20 +16,30 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import java.util.List; import java.util.UUID; public interface OAuth2Service { - List getOAuth2Clients(String domainScheme, String domainName); + List getOAuth2Clients(String domainScheme, String domainName, String pkgName); + @Deprecated void saveOAuth2Params(OAuth2ClientsParams oauth2Params); + @Deprecated OAuth2ClientsParams findOAuth2Params(); - OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id); + void saveOAuth2Info(OAuth2Info oauth2Info); - List findAllClientRegistrationInfos(); + OAuth2Info findOAuth2Info(); + + OAuth2Registration findRegistration(UUID id); + + List findAllRegistrations(); + + String findCallbackUrlScheme(UUID registrationId, String pkgName); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java new file mode 100644 index 0000000000..220232f16d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2DomainId extends UUIDBased { + + @JsonCreator + public OAuth2DomainId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2DomainId fromString(String oauth2DomainId) { + return new OAuth2DomainId(UUID.fromString(oauth2DomainId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java new file mode 100644 index 0000000000..934d1f72a6 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2MobileId extends UUIDBased { + + @JsonCreator + public OAuth2MobileId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2MobileId fromString(String oauth2MobileId) { + return new OAuth2MobileId(UUID.fromString(oauth2MobileId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java new file mode 100644 index 0000000000..3aa0c5bb3d --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ParamsId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2ParamsId extends UUIDBased { + + @JsonCreator + public OAuth2ParamsId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2ParamsId fromString(String oauth2ParamsId) { + return new OAuth2ParamsId(UUID.fromString(oauth2ParamsId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java new file mode 100644 index 0000000000..0019ef1a04 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.UUID; + +public class OAuth2RegistrationId extends UUIDBased { + + @JsonCreator + public OAuth2RegistrationId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2RegistrationId fromString(String oauth2RegistrationId) { + return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId)); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java index 88d4245dc7..616fc3b29c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationId.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.id; +package org.thingsboard.server.common.data.id.deprecated; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.id.UUIDBased; import java.util.UUID; +@Deprecated public class OAuth2ClientRegistrationId extends UUIDBased { @JsonCreator diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java similarity index 89% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java index 2e34eaf5ac..6ba959318c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/deprecated/OAuth2ClientRegistrationInfoId.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.id; +package org.thingsboard.server.common.data.id.deprecated; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.id.UUIDBased; import java.util.UUID; +@Deprecated public class OAuth2ClientRegistrationInfoId extends UUIDBased { @JsonCreator diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index f75fb2aaa7..b46f23484f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -20,10 +20,8 @@ import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; -import org.thingsboard.server.common.data.id.TenantId; import java.util.List; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java new file mode 100644 index 0000000000..0dee447023 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2DomainId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Domain extends BaseData { + + private OAuth2ParamsId oauth2ParamsId; + private String domainName; + private SchemeType domainScheme; + + public OAuth2Domain(OAuth2Domain domain) { + super(domain); + this.oauth2ParamsId = domain.oauth2ParamsId; + this.domainName = domain.domainName; + this.domainScheme = domain.domainScheme; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java new file mode 100644 index 0000000000..9d9bd939da --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@EqualsAndHashCode +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class OAuth2DomainInfo { + private SchemeType scheme; + private String name; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java new file mode 100644 index 0000000000..72f4b06161 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java @@ -0,0 +1,31 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.*; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2Info { + private boolean enabled; + private List oauth2ParamsInfos; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java new file mode 100644 index 0000000000..b3249a1ed5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java @@ -0,0 +1,42 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2MobileId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Mobile extends BaseData { + + private OAuth2ParamsId oauth2ParamsId; + private String pkgName; + private String callbackUrlScheme; + + public OAuth2Mobile(OAuth2Mobile mobile) { + super(mobile); + this.oauth2ParamsId = mobile.oauth2ParamsId; + this.pkgName = mobile.pkgName; + this.callbackUrlScheme = mobile.callbackUrlScheme; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java new file mode 100644 index 0000000000..18fbd49dcd --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java @@ -0,0 +1,34 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@EqualsAndHashCode +@Data +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class OAuth2MobileInfo { + private String pkgName; + private String callbackUrlScheme; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java new file mode 100644 index 0000000000..570fe6cb20 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Params.java @@ -0,0 +1,40 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class OAuth2Params extends BaseData { + + private boolean enabled; + private TenantId tenantId; + + public OAuth2Params(OAuth2Params oauth2Params) { + super(oauth2Params); + this.enabled = oauth2Params.enabled; + this.tenantId = oauth2Params.tenantId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java new file mode 100644 index 0000000000..1a1d729d6b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java @@ -0,0 +1,39 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2ParamsInfo { + + private List domainInfos; + private List mobileInfos; + private List clientRegistrations; + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java new file mode 100644 index 0000000000..5120e3202f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java @@ -0,0 +1,77 @@ +/** + * Copyright © 2016-2021 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.oauth2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; + +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString(exclude = {"clientSecret"}) +@NoArgsConstructor +public class OAuth2Registration extends SearchTextBasedWithAdditionalInfo implements HasName { + + private OAuth2ParamsId oauth2ParamsId; + private OAuth2MapperConfig mapperConfig; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String loginButtonLabel; + private String loginButtonIcon; + + public OAuth2Registration(OAuth2Registration registration) { + super(registration); + this.oauth2ParamsId = registration.oauth2ParamsId; + this.mapperConfig = registration.mapperConfig; + this.clientId = registration.clientId; + this.clientSecret = registration.clientSecret; + this.authorizationUri = registration.authorizationUri; + this.accessTokenUri = registration.accessTokenUri; + this.scope = registration.scope; + this.userInfoUri = registration.userInfoUri; + this.userNameAttributeName = registration.userNameAttributeName; + this.jwkSetUri = registration.jwkSetUri; + this.clientAuthenticationMethod = registration.clientAuthenticationMethod; + this.loginButtonLabel = registration.loginButtonLabel; + this.loginButtonIcon = registration.loginButtonIcon; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + public String getName() { + return loginButtonLabel; + } + + @Override + public String getSearchText() { + return getName(); + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java index 2598013ab6..449977f106 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java @@ -17,7 +17,6 @@ package org.thingsboard.server.common.data.oauth2; import com.fasterxml.jackson.databind.JsonNode; import lombok.*; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; import java.util.List; @@ -27,7 +26,7 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor @Builder -public class ClientRegistrationDto { +public class OAuth2RegistrationInfo { private OAuth2MapperConfig mapperConfig; private String clientId; private String clientSecret; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java new file mode 100644 index 0000000000..4f491a2d8a --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ClientRegistrationDto.java @@ -0,0 +1,45 @@ +/** + * Copyright © 2016-2021 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.oauth2.deprecated; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.*; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; + +import java.util.List; + +@Deprecated +@EqualsAndHashCode +@Data +@ToString(exclude = {"clientSecret"}) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ClientRegistrationDto { + private OAuth2MapperConfig mapperConfig; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String loginButtonLabel; + private String loginButtonIcon; + private JsonNode additionalInfo; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java index fd6b93100c..5078c4483f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/DomainInfo.java @@ -13,10 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; +import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java similarity index 85% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java index 83647283a0..d071a6f1d4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/ExtendedOAuth2ClientRegistrationInfo.java @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java similarity index 81% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java index d0fa6648c8..cfac2de329 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistration.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java index f70000693c..a99f56689b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientRegistrationInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @@ -22,10 +22,12 @@ import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import java.util.List; +@Deprecated @EqualsAndHashCode(callSuper = true) @Data @ToString(exclude = {"clientSecret"}) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java index 1570f71107..ff17fe2819 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsDomainParams.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; import java.util.List; -import java.util.Set; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java similarity index 92% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java index 57c7351295..4f7735ef04 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/deprecated/OAuth2ClientsParams.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.oauth2.deprecated; import lombok.*; import java.util.List; -import java.util.Set; +@Deprecated @EqualsAndHashCode @Data @ToString diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index bfb530dd35..02c8db42d4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -408,10 +408,20 @@ public class ModelConstants { /** * OAuth2 client registration constants. */ - public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + + public static final String OAUTH2_PARAMS_COLUMN_FAMILY_NAME = "oauth2_params"; + public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled"; + public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + + public static final String OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_registration"; + public static final String OAUTH2_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_domain"; + public static final String OAUTH2_MOBILE_COLUMN_FAMILY_NAME = "oauth2_mobile"; + public static final String OAUTH2_PARAMS_ID_PROPERTY = "oauth2_params_id"; + public static final String OAUTH2_PKG_NAME_PROPERTY = "pkg_name"; + public static final String OAUTH2_CALLBACK_URL_SCHEME_PROPERTY = "callback_url_scheme"; + public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info"; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; - public static final String OAUTH2_CLIENT_REGISTRATION_TO_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_client_registration_to_domain"; public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template"; public static final String OAUTH2_ENABLED_PROPERTY = "enabled"; public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; @@ -422,7 +432,6 @@ public class ModelConstants { public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri"; - public static final String OAUTH2_REDIRECT_URI_TEMPLATE_PROPERTY = "redirect_uri_template"; public static final String OAUTH2_SCOPE_PROPERTY = "scope"; public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java new file mode 100644 index 0000000000..f1dbe6fff7 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java @@ -0,0 +1,76 @@ +/** + * Copyright © 2016-2021 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.OAuth2DomainId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_DOMAIN_COLUMN_FAMILY_NAME) +public class OAuth2DomainEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + + @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) + private String domainName; + + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY) + private SchemeType domainScheme; + + public OAuth2DomainEntity() { + super(); + } + + public OAuth2DomainEntity(OAuth2Domain domain) { + if (domain.getId() != null) { + this.setUuid(domain.getId().getId()); + } + this.setCreatedTime(domain.getCreatedTime()); + if (domain.getOauth2ParamsId() != null) { + this.oauth2ParamsId = domain.getOauth2ParamsId().getId(); + } + this.domainName = domain.getDomainName(); + this.domainScheme = domain.getDomainScheme(); + } + + @Override + public OAuth2Domain toData() { + OAuth2Domain domain = new OAuth2Domain(); + domain.setId(new OAuth2DomainId(id)); + domain.setCreatedTime(createdTime); + domain.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + domain.setDomainName(domainName); + domain.setDomainScheme(domainScheme); + return domain; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java new file mode 100644 index 0000000000..403f7af958 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java @@ -0,0 +1,72 @@ +/** + * Copyright © 2016-2021 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.id.OAuth2MobileId; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_MOBILE_COLUMN_FAMILY_NAME) +public class OAuth2MobileEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + + @Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY) + private String pkgName; + + @Column(name = ModelConstants.OAUTH2_CALLBACK_URL_SCHEME_PROPERTY) + private String callbackUrlScheme; + + public OAuth2MobileEntity() { + super(); + } + + public OAuth2MobileEntity(OAuth2Mobile mobile) { + if (mobile.getId() != null) { + this.setUuid(mobile.getId().getId()); + } + this.setCreatedTime(mobile.getCreatedTime()); + if (mobile.getOauth2ParamsId() != null) { + this.oauth2ParamsId = mobile.getOauth2ParamsId().getId(); + } + this.pkgName = mobile.getPkgName(); + this.callbackUrlScheme = mobile.getCallbackUrlScheme(); + } + + @Override + public OAuth2Mobile toData() { + OAuth2Mobile mobile = new OAuth2Mobile(); + mobile.setId(new OAuth2MobileId(id)); + mobile.setCreatedTime(createdTime); + mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + mobile.setPkgName(pkgName); + mobile.setCallbackUrlScheme(callbackUrlScheme); + return mobile; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java new file mode 100644 index 0000000000..f2c893f6f3 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java @@ -0,0 +1,65 @@ +/** + * Copyright © 2016-2021 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.dao.model.sql; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Table; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.OAUTH2_PARAMS_COLUMN_FAMILY_NAME) +@NoArgsConstructor +public class OAuth2ParamsEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY) + private Boolean enabled; + + @Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY) + private UUID tenantId; + + public OAuth2ParamsEntity(OAuth2Params oauth2Params) { + if (oauth2Params.getId() != null) { + this.setUuid(oauth2Params.getUuidId()); + } + this.setCreatedTime(oauth2Params.getCreatedTime()); + this.enabled = oauth2Params.isEnabled(); + if (oauth2Params.getTenantId() != null) { + this.tenantId = oauth2Params.getTenantId().getId(); + } + } + + @Override + public OAuth2Params toData() { + OAuth2Params oauth2Params = new OAuth2Params(); + oauth2Params.setId(new OAuth2ParamsId(id)); + oauth2Params.setCreatedTime(createdTime); + oauth2Params.setTenantId(new TenantId(tenantId)); + oauth2Params.setEnabled(enabled); + return oauth2Params; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java new file mode 100644 index 0000000000..9c6c260316 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java @@ -0,0 +1,212 @@ +/** + * Copyright © 2016-2021 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.dao.model.sql; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.Table; +import java.util.Arrays; +import java.util.UUID; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.OAUTH2_REGISTRATION_COLUMN_FAMILY_NAME) +public class OAuth2RegistrationEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) + private UUID oauth2ParamsId; + @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) + private String clientId; + @Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) + private String clientSecret; + @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) + private String authorizationUri; + @Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) + private String tokenUri; + @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) + private String scope; + @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) + private String userInfoUri; + @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) + private String userNameAttributeName; + @Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) + private String jwkSetUri; + @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) + private String clientAuthenticationMethod; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY) + private String loginButtonLabel; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY) + private String loginButtonIcon; + @Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY) + private Boolean allowUserCreation; + @Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY) + private Boolean activateUser; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) + private MapperType type; + @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) + private String emailAttributeKey; + @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) + private String firstNameAttributeKey; + @Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) + private String lastNameAttributeKey; + @Enumerated(EnumType.STRING) + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) + private TenantNameStrategyType tenantNameStrategy; + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) + private String tenantNamePattern; + @Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) + private String customerNamePattern; + @Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) + private String defaultDashboardName; + @Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) + private Boolean alwaysFullScreen; + @Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY) + private String url; + @Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY) + private String username; + @Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY) + private String password; + @Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY) + private Boolean sendToken; + + @Type(type = "json") + @Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY) + private JsonNode additionalInfo; + + public OAuth2RegistrationEntity() { + super(); + } + + public OAuth2RegistrationEntity(OAuth2Registration registration) { + if (registration.getId() != null) { + this.setUuid(registration.getId().getId()); + } + this.setCreatedTime(registration.getCreatedTime()); + if (registration.getOauth2ParamsId() != null) { + this.oauth2ParamsId = registration.getOauth2ParamsId().getId(); + } + this.clientId = registration.getClientId(); + this.clientSecret = registration.getClientSecret(); + this.authorizationUri = registration.getAuthorizationUri(); + this.tokenUri = registration.getAccessTokenUri(); + this.scope = registration.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); + this.userInfoUri = registration.getUserInfoUri(); + this.userNameAttributeName = registration.getUserNameAttributeName(); + this.jwkSetUri = registration.getJwkSetUri(); + this.clientAuthenticationMethod = registration.getClientAuthenticationMethod(); + this.loginButtonLabel = registration.getLoginButtonLabel(); + this.loginButtonIcon = registration.getLoginButtonIcon(); + this.additionalInfo = registration.getAdditionalInfo(); + OAuth2MapperConfig mapperConfig = registration.getMapperConfig(); + if (mapperConfig != null) { + this.allowUserCreation = mapperConfig.isAllowUserCreation(); + this.activateUser = mapperConfig.isActivateUser(); + this.type = mapperConfig.getType(); + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig != null) { + this.emailAttributeKey = basicConfig.getEmailAttributeKey(); + this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); + this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); + this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); + this.tenantNamePattern = basicConfig.getTenantNamePattern(); + this.customerNamePattern = basicConfig.getCustomerNamePattern(); + this.defaultDashboardName = basicConfig.getDefaultDashboardName(); + this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); + } + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig != null) { + this.url = customConfig.getUrl(); + this.username = customConfig.getUsername(); + this.password = customConfig.getPassword(); + this.sendToken = customConfig.isSendToken(); + } + } + } + + @Override + public OAuth2Registration toData() { + OAuth2Registration registration = new OAuth2Registration(); + registration.setId(new OAuth2RegistrationId(id)); + registration.setCreatedTime(createdTime); + registration.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + registration.setAdditionalInfo(additionalInfo); + registration.setMapperConfig( + OAuth2MapperConfig.builder() + .allowUserCreation(allowUserCreation) + .activateUser(activateUser) + .type(type) + .basic( + (type == MapperType.BASIC || type == MapperType.GITHUB) ? + OAuth2BasicMapperConfig.builder() + .emailAttributeKey(emailAttributeKey) + .firstNameAttributeKey(firstNameAttributeKey) + .lastNameAttributeKey(lastNameAttributeKey) + .tenantNameStrategy(tenantNameStrategy) + .tenantNamePattern(tenantNamePattern) + .customerNamePattern(customerNamePattern) + .defaultDashboardName(defaultDashboardName) + .alwaysFullScreen(alwaysFullScreen) + .build() + : null + ) + .custom( + type == MapperType.CUSTOM ? + OAuth2CustomMapperConfig.builder() + .url(url) + .username(username) + .password(password) + .sendToken(sendToken) + .build() + : null + ) + .build() + ); + registration.setClientId(clientId); + registration.setClientSecret(clientSecret); + registration.setAuthorizationUri(authorizationUri); + registration.setAccessTokenUri(tokenUri); + registration.setScope(Arrays.asList(scope.split(","))); + registration.setUserInfoUri(userInfoUri); + registration.setUserNameAttributeName(userNameAttributeName); + registration.setJwkSetUri(jwkSetUri); + registration.setClientAuthenticationMethod(clientAuthenticationMethod); + registration.setLoginButtonLabel(loginButtonLabel); + registration.setLoginButtonIcon(loginButtonIcon); + return registration; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java similarity index 97% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java index 4799087457..616f128920 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/AbstractOAuth2ClientRegistrationInfoEntity.java @@ -13,22 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import com.fasterxml.jackson.databind.JsonNode; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.*; import java.util.Arrays; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @TypeDef(name = "json", typeClass = JsonStringType.class) diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java similarity index 91% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java index 129bcf730b..beb525e2e0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/ExtendedOAuth2ClientRegistrationInfoEntity.java @@ -13,13 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java similarity index 89% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java index 505f2facf1..9de0b9daea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationEntity.java @@ -13,22 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.*; -import java.util.Arrays; import java.util.UUID; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java similarity index 91% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java index 1a379baf23..fbb9ec3fca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/deprecated/OAuth2ClientRegistrationInfoEntity.java @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.model.sql; +package org.thingsboard.server.dao.model.sql.deprecated; import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Entity; import javax.persistence.Table; +@Deprecated @Data @EqualsAndHashCode(callSuper = true) @Entity diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 7361dc21d2..feba5c0158 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -21,7 +21,7 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import java.util.UUID; @@ -34,25 +34,25 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep @Override public ClientRegistration findByRegistrationId(String registrationId) { - OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId)); - return oAuth2ClientRegistrationInfo == null ? - null : toSpringClientRegistration(oAuth2ClientRegistrationInfo); + OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(registrationId)); + return registration == null ? + null : toSpringClientRegistration(registration); } - private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){ - String registrationId = localClientRegistration.getUuidId().toString(); + private ClientRegistration toSpringClientRegistration(OAuth2Registration registration){ + String registrationId = registration.getUuidId().toString(); return ClientRegistration.withRegistrationId(registrationId) - .clientName(localClientRegistration.getName()) - .clientId(localClientRegistration.getClientId()) - .authorizationUri(localClientRegistration.getAuthorizationUri()) - .clientSecret(localClientRegistration.getClientSecret()) - .tokenUri(localClientRegistration.getAccessTokenUri()) - .scope(localClientRegistration.getScope()) + .clientName(registration.getName()) + .clientId(registration.getClientId()) + .authorizationUri(registration.getAuthorizationUri()) + .clientSecret(registration.getClientSecret()) + .tokenUri(registration.getAccessTokenUri()) + .scope(registration.getScope()) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .userInfoUri(localClientRegistration.getUserInfoUri()) - .userNameAttributeName(localClientRegistration.getUserNameAttributeName()) - .jwkSetUri(localClientRegistration.getJwkSetUri()) - .clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) + .userInfoUri(registration.getUserInfoUri()) + .userNameAttributeName(registration.getUserNameAttributeName()) + .jwkSetUri(registration.getJwkSetUri()) + .clientAuthenticationMethod(new ClientAuthenticationMethod(registration.getClientAuthenticationMethod())) .redirectUri(defaultRedirectUriTemplate) .build(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java new file mode 100644 index 0000000000..78c237d943 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 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.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2DomainDao extends Dao { + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java new file mode 100644 index 0000000000..3bccb61b0b --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 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.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2MobileDao extends Dao { + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java new file mode 100644 index 0000000000..5821431444 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ParamsDao.java @@ -0,0 +1,23 @@ +/** + * Copyright © 2016-2021 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.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.Dao; + +public interface OAuth2ParamsDao extends Dao { + void deleteAll(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java new file mode 100644 index 0000000000..ad12b12e48 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2021 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.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.Dao; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2RegistrationDao extends Dao { + + List findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName); + + List findByOAuth2ParamsId(UUID oauth2ParamsId); + + String findCallbackUrlScheme(UUID id, String pkgName); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index acde25f8ed..8d06adee0b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -19,11 +19,21 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.ClientRegistrationDto; +import org.thingsboard.server.common.data.oauth2.deprecated.DomainInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsDomainParams; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationDao; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationInfoDao; import javax.transaction.Transactional; import java.util.*; @@ -45,9 +55,17 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se private OAuth2ClientRegistrationInfoDao clientRegistrationInfoDao; @Autowired private OAuth2ClientRegistrationDao clientRegistrationDao; + @Autowired + private OAuth2ParamsDao oauth2ParamsDao; + @Autowired + private OAuth2RegistrationDao oauth2RegistrationDao; + @Autowired + private OAuth2DomainDao oauth2DomainDao; + @Autowired + private OAuth2MobileDao oauth2MobileDao; @Override - public List getOAuth2Clients(String domainSchemeStr, String domainName) { + public List getOAuth2Clients(String domainSchemeStr, String domainName, String pkgName) { log.trace("Executing getOAuth2Clients [{}://{}]", domainSchemeStr, domainName); if (domainSchemeStr == null) { throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); @@ -59,12 +77,12 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); } validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); - return clientRegistrationInfoDao.findByDomainSchemesAndDomainName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName).stream() - .filter(OAuth2ClientRegistrationInfo::isEnabled) + return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName).stream() .map(OAuth2Utils::toClientInfo) .collect(Collectors.toList()); } + @Deprecated @Override @Transactional public void saveOAuth2Params(OAuth2ClientsParams oauth2Params) { @@ -85,6 +103,33 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se }); } + @Override + @Transactional + public void saveOAuth2Info(OAuth2Info oauth2Info) { + log.trace("Executing saveOAuth2Info [{}]", oauth2Info); + oauth2InfoValidator.accept(oauth2Info); + oauth2ParamsDao.deleteAll(); + oauth2Info.getOauth2ParamsInfos().forEach(oauth2ParamsInfo -> { + OAuth2Params oauth2Params = OAuth2Utils.infoToOAuth2Params(oauth2Info); + OAuth2Params savedOauth2Params = oauth2ParamsDao.save(TenantId.SYS_TENANT_ID, oauth2Params); + oauth2ParamsInfo.getClientRegistrations().forEach(registrationInfo -> { + OAuth2Registration registration = OAuth2Utils.toOAuth2Registration(savedOauth2Params.getId(), registrationInfo); + oauth2RegistrationDao.save(TenantId.SYS_TENANT_ID, registration); + }); + oauth2ParamsInfo.getDomainInfos().forEach(domainInfo -> { + OAuth2Domain domain = OAuth2Utils.toOAuth2Domain(savedOauth2Params.getId(), domainInfo); + oauth2DomainDao.save(TenantId.SYS_TENANT_ID, domain); + }); + if (oauth2ParamsInfo.getMobileInfos() != null) { + oauth2ParamsInfo.getMobileInfos().forEach(mobileInfo -> { + OAuth2Mobile mobile = OAuth2Utils.toOAuth2Mobile(savedOauth2Params.getId(), mobileInfo); + oauth2MobileDao.save(TenantId.SYS_TENANT_ID, mobile); + }); + } + }); + } + + @Deprecated @Override public OAuth2ClientsParams findOAuth2Params() { log.trace("Executing findOAuth2Params"); @@ -93,16 +138,42 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - public OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id) { - log.trace("Executing findClientRegistrationInfo [{}]", id); + public OAuth2Info findOAuth2Info() { + log.trace("Executing findOAuth2Info"); + OAuth2Info oauth2Info = new OAuth2Info(); + List oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID); + oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(param -> param.isEnabled())); + List oauth2ParamsInfos = new ArrayList<>(); + oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos); + oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> { + List registrations = oauth2RegistrationDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + List domains = oauth2DomainDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + List mobiles = oauth2MobileDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); + oauth2ParamsInfos.add(OAuth2Utils.toOAuth2ParamsInfo(registrations, domains, mobiles)); + }); + return oauth2Info; + } + + @Override + public OAuth2Registration findRegistration(UUID id) { + log.trace("Executing findRegistration [{}]", id); + validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + return oauth2RegistrationDao.findById(null, id); + } + + @Override + public String findCallbackUrlScheme(UUID id, String pkgName) { + log.trace("Executing findCallbackUrlScheme [{}][{}]", id, pkgName); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); - return clientRegistrationInfoDao.findById(null, id); + validateString(pkgName, "Incorrect package name"); + return oauth2RegistrationDao.findCallbackUrlScheme(id, pkgName); } + @Override - public List findAllClientRegistrationInfos() { - log.trace("Executing findAllClientRegistrationInfos"); - return clientRegistrationInfoDao.findAll(); + public List findAllRegistrations() { + log.trace("Executing findAllRegistrations"); + return oauth2RegistrationDao.find(TenantId.SYS_TENANT_ID); } private final Consumer clientParamsValidator = oauth2Params -> { @@ -212,4 +283,136 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } }; + + private final Consumer oauth2InfoValidator = oauth2Info -> { + if (oauth2Info == null + || oauth2Info.getOauth2ParamsInfos() == null) { + throw new DataValidationException("OAuth2 param infos should be specified!"); + } + for (OAuth2ParamsInfo oauth2Params : oauth2Info.getOauth2ParamsInfos()) { + if (oauth2Params.getDomainInfos() == null + || oauth2Params.getDomainInfos().isEmpty()) { + throw new DataValidationException("List of domain configuration should be specified!"); + } + for (OAuth2DomainInfo domainInfo : oauth2Params.getDomainInfos()) { + if (StringUtils.isEmpty(domainInfo.getName())) { + throw new DataValidationException("Domain name should be specified!"); + } + if (domainInfo.getScheme() == null) { + throw new DataValidationException("Domain scheme should be specified!"); + } + } + oauth2Params.getDomainInfos().stream() + .collect(Collectors.groupingBy(OAuth2DomainInfo::getName)) + .forEach((domainName, domainInfos) -> { + if (domainInfos.size() > 1 && domainInfos.stream().anyMatch(domainInfo -> domainInfo.getScheme() == SchemeType.MIXED)) { + throw new DataValidationException("MIXED scheme type shouldn't be combined with another scheme type!"); + } + domainInfos.stream() + .collect(Collectors.groupingBy(OAuth2DomainInfo::getScheme)) + .forEach((schemeType, domainInfosBySchemeType) -> { + if (domainInfosBySchemeType.size() > 1) { + throw new DataValidationException("Domain name and protocol must be unique within OAuth2 parameters!"); + } + }); + }); + if (oauth2Params.getMobileInfos() != null) { + for (OAuth2MobileInfo mobileInfo : oauth2Params.getMobileInfos()) { + if (StringUtils.isEmpty(mobileInfo.getPkgName())) { + throw new DataValidationException("Package should be specified!"); + } + if (StringUtils.isEmpty(mobileInfo.getCallbackUrlScheme())) { + throw new DataValidationException("Callback URL scheme should be specified!"); + } + } + oauth2Params.getMobileInfos().stream() + .collect(Collectors.groupingBy(OAuth2MobileInfo::getPkgName)) + .forEach((pkgName, mobileInfos) -> { + if (mobileInfos.size() > 1) { + throw new DataValidationException("Mobile app package name must be unique within OAuth2 parameters!"); + } + }); + } + if (oauth2Params.getClientRegistrations() == null || oauth2Params.getClientRegistrations().isEmpty()) { + throw new DataValidationException("Client registrations should be specified!"); + } + for (OAuth2RegistrationInfo clientRegistration : oauth2Params.getClientRegistrations()) { + if (StringUtils.isEmpty(clientRegistration.getClientId())) { + throw new DataValidationException("Client ID should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientSecret())) { + throw new DataValidationException("Client secret should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) { + throw new DataValidationException("Authorization uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { + throw new DataValidationException("Token uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getScope())) { + throw new DataValidationException("Scope should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) { + throw new DataValidationException("User info uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { + throw new DataValidationException("User name attribute name should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { + throw new DataValidationException("Client authentication method should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) { + throw new DataValidationException("Login button label should be specified!"); + } + OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); + if (mapperConfig == null) { + throw new DataValidationException("Mapper config should be specified!"); + } + if (mapperConfig.getType() == null) { + throw new DataValidationException("Mapper config type should be specified!"); + } + if (mapperConfig.getType() == MapperType.BASIC) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.GITHUB) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (!StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key cannot be configured for GITHUB mapper type!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig == null) { + throw new DataValidationException("Custom config should be specified!"); + } + if (StringUtils.isEmpty(customConfig.getUrl())) { + throw new DataValidationException("Custom mapper URL should be specified!"); + } + } + } + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 2a59ccb829..dd128b435b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -15,19 +15,30 @@ */ package org.thingsboard.server.dao.oauth2; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.deprecated.OAuth2ClientRegistrationInfoId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.deprecated.ClientRegistrationDto; +import org.thingsboard.server.common.data.oauth2.deprecated.DomainInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsDomainParams; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientsParams; import java.util.*; +import java.util.stream.Collectors; public class OAuth2Utils { public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistrationInfo clientRegistrationInfo) { + public static OAuth2ClientInfo toClientInfo(OAuth2Registration registration) { OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistrationInfo.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistrationInfo.getUuidId().toString())); - client.setIcon(clientRegistrationInfo.getLoginButtonIcon()); + client.setName(registration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, registration.getUuidId().toString())); + client.setIcon(registration.getLoginButtonIcon()); return client; } @@ -99,4 +110,127 @@ public class OAuth2Utils { clientRegistration.setDomainScheme(domainScheme); return clientRegistration; } + + public static OAuth2ParamsInfo toOAuth2ParamsInfo(List registrations, List domains, List mobiles) { + OAuth2ParamsInfo oauth2ParamsInfo = new OAuth2ParamsInfo(); + oauth2ParamsInfo.setClientRegistrations(registrations.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2RegistrationInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setDomainInfos(domains.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2DomainInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setMobileInfos(mobiles.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2MobileInfo).collect(Collectors.toList())); + return oauth2ParamsInfo; + } + + public static OAuth2RegistrationInfo toOAuth2RegistrationInfo(OAuth2Registration registration) { + return OAuth2RegistrationInfo.builder() + .mapperConfig(registration.getMapperConfig()) + .clientId(registration.getClientId()) + .clientSecret(registration.getClientSecret()) + .authorizationUri(registration.getAuthorizationUri()) + .accessTokenUri(registration.getAccessTokenUri()) + .scope(registration.getScope()) + .userInfoUri(registration.getUserInfoUri()) + .userNameAttributeName(registration.getUserNameAttributeName()) + .jwkSetUri(registration.getJwkSetUri()) + .clientAuthenticationMethod(registration.getClientAuthenticationMethod()) + .loginButtonLabel(registration.getLoginButtonLabel()) + .loginButtonIcon(registration.getLoginButtonIcon()) + .additionalInfo(registration.getAdditionalInfo()) + .build(); + } + + public static OAuth2DomainInfo toOAuth2DomainInfo(OAuth2Domain domain) { + return OAuth2DomainInfo.builder() + .name(domain.getDomainName()) + .scheme(domain.getDomainScheme()) + .build(); + } + + public static OAuth2MobileInfo toOAuth2MobileInfo(OAuth2Mobile mobile) { + return OAuth2MobileInfo.builder() + .pkgName(mobile.getPkgName()) + .callbackUrlScheme(mobile.getCallbackUrlScheme()) + .build(); + } + + public static OAuth2Params infoToOAuth2Params(OAuth2Info oauth2Info) { + OAuth2Params oauth2Params = new OAuth2Params(); + oauth2Params.setEnabled(oauth2Info.isEnabled()); + oauth2Params.setTenantId(TenantId.SYS_TENANT_ID); + return oauth2Params; + } + + public static OAuth2Registration toOAuth2Registration(OAuth2ParamsId oauth2ParamsId, OAuth2RegistrationInfo registrationInfo) { + OAuth2Registration registration = new OAuth2Registration(); + registration.setOauth2ParamsId(oauth2ParamsId); + registration.setMapperConfig(registrationInfo.getMapperConfig()); + registration.setClientId(registrationInfo.getClientId()); + registration.setClientSecret(registrationInfo.getClientSecret()); + registration.setAuthorizationUri(registrationInfo.getAuthorizationUri()); + registration.setAccessTokenUri(registrationInfo.getAccessTokenUri()); + registration.setScope(registrationInfo.getScope()); + registration.setUserInfoUri(registrationInfo.getUserInfoUri()); + registration.setUserNameAttributeName(registrationInfo.getUserNameAttributeName()); + registration.setJwkSetUri(registrationInfo.getJwkSetUri()); + registration.setClientAuthenticationMethod(registrationInfo.getClientAuthenticationMethod()); + registration.setLoginButtonLabel(registrationInfo.getLoginButtonLabel()); + registration.setLoginButtonIcon(registrationInfo.getLoginButtonIcon()); + registration.setAdditionalInfo(registrationInfo.getAdditionalInfo()); + return registration; + } + + public static OAuth2Domain toOAuth2Domain(OAuth2ParamsId oauth2ParamsId, OAuth2DomainInfo domainInfo) { + OAuth2Domain domain = new OAuth2Domain(); + domain.setOauth2ParamsId(oauth2ParamsId); + domain.setDomainName(domainInfo.getName()); + domain.setDomainScheme(domainInfo.getScheme()); + return domain; + } + + public static OAuth2Mobile toOAuth2Mobile(OAuth2ParamsId oauth2ParamsId, OAuth2MobileInfo mobileInfo) { + OAuth2Mobile mobile = new OAuth2Mobile(); + mobile.setOauth2ParamsId(oauth2ParamsId); + mobile.setPkgName(mobileInfo.getPkgName()); + mobile.setCallbackUrlScheme(mobileInfo.getCallbackUrlScheme()); + return mobile; + } + + @Deprecated + public static OAuth2Info clientParamsToOAuth2Info(OAuth2ClientsParams clientsParams) { + OAuth2Info oauth2Info = new OAuth2Info(); + oauth2Info.setEnabled(clientsParams.isEnabled()); + oauth2Info.setOauth2ParamsInfos(clientsParams.getDomainsParams().stream().map(OAuth2Utils::clientsDomainParamsToOAuth2ParamsInfo).collect(Collectors.toList())); + return oauth2Info; + } + + private static OAuth2ParamsInfo clientsDomainParamsToOAuth2ParamsInfo(OAuth2ClientsDomainParams clientsDomainParams) { + OAuth2ParamsInfo oauth2ParamsInfo = new OAuth2ParamsInfo(); + oauth2ParamsInfo.setMobileInfos(Collections.emptyList()); + oauth2ParamsInfo.setClientRegistrations(clientsDomainParams.getClientRegistrations().stream().map(OAuth2Utils::clientRegistrationDtoToOAuth2RegistrationInfo).collect(Collectors.toList())); + oauth2ParamsInfo.setDomainInfos(clientsDomainParams.getDomainInfos().stream().map(OAuth2Utils::domainInfoToOAuth2DomainInfo).collect(Collectors.toList())); + return oauth2ParamsInfo; + } + + private static OAuth2RegistrationInfo clientRegistrationDtoToOAuth2RegistrationInfo(ClientRegistrationDto clientRegistrationDto) { + return OAuth2RegistrationInfo.builder() + .mapperConfig(clientRegistrationDto.getMapperConfig()) + .clientId(clientRegistrationDto.getClientId()) + .clientSecret(clientRegistrationDto.getClientSecret()) + .authorizationUri(clientRegistrationDto.getAuthorizationUri()) + .accessTokenUri(clientRegistrationDto.getAccessTokenUri()) + .scope(clientRegistrationDto.getScope()) + .userInfoUri(clientRegistrationDto.getUserInfoUri()) + .userNameAttributeName(clientRegistrationDto.getUserNameAttributeName()) + .jwkSetUri(clientRegistrationDto.getJwkSetUri()) + .clientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod()) + .loginButtonLabel(clientRegistrationDto.getLoginButtonLabel()) + .loginButtonIcon(clientRegistrationDto.getLoginButtonIcon()) + .additionalInfo(clientRegistrationDto.getAdditionalInfo()) + .build(); + } + + private static OAuth2DomainInfo domainInfoToOAuth2DomainInfo(DomainInfo domainInfo) { + return OAuth2DomainInfo.builder() + .name(domainInfo.getName()) + .scheme(domainInfo.getScheme()) + .build(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java similarity index 83% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java rename to dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java index 2f4f2c1be2..63b2c46034 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationDao.java @@ -13,11 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.dao.oauth2.deprecated; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; import org.thingsboard.server.dao.Dao; +@Deprecated public interface OAuth2ClientRegistrationDao extends Dao { void deleteAll(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java index 6fe93bacf4..9f44335602 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/deprecated/OAuth2ClientRegistrationInfoDao.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.dao.oauth2.deprecated; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.Dao; import java.util.List; -import java.util.Set; +@Deprecated public interface OAuth2ClientRegistrationInfoDao extends Dao { List findAll(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java new file mode 100644 index 0000000000..0d74821678 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Domain; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; +import org.thingsboard.server.dao.oauth2.OAuth2DomainDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2DomainDao extends JpaAbstractDao implements OAuth2DomainDao { + + private final OAuth2DomainRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2DomainEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java new file mode 100644 index 0000000000..6f216b23f2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; +import org.thingsboard.server.dao.oauth2.OAuth2MobileDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2MobileDao extends JpaAbstractDao implements OAuth2MobileDao { + + private final OAuth2MobileRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2MobileEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java new file mode 100644 index 0000000000..2f28cb58f6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java @@ -0,0 +1,47 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Params; +import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; +import org.thingsboard.server.dao.oauth2.OAuth2ParamsDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2ParamsDao extends JpaAbstractDao implements OAuth2ParamsDao { + private final OAuth2ParamsRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2ParamsEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public void deleteAll() { + repository.deleteAll(); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java new file mode 100644 index 0000000000..c3ee8a8c03 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java @@ -0,0 +1,62 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; +import org.thingsboard.server.dao.oauth2.OAuth2RegistrationDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2RegistrationDao extends JpaAbstractDao implements OAuth2RegistrationDao { + + private final OAuth2RegistrationRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2RegistrationEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findEnabledByDomainSchemesDomainNameAndPkgName(List domainSchemes, String domainName, String pkgName) { + return DaoUtil.convertDataList(repository.findAllEnabledByDomainSchemesNameAndPkgName(domainSchemes, domainName, pkgName)); + } + + @Override + public List findByOAuth2ParamsId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + } + + @Override + public String findCallbackUrlScheme(UUID id, String pkgName) { + return repository.findCallbackUrlScheme(id, pkgName); + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java new file mode 100644 index 0000000000..26d084f613 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2DomainRepository extends CrudRepository { + + List findByOauth2ParamsId(UUID oauth2ParamsId); + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java new file mode 100644 index 0000000000..d4660ef2ee --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2MobileRepository extends CrudRepository { + + List findByOauth2ParamsId(UUID oauth2ParamsId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java new file mode 100644 index 0000000000..8e53b34e31 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java @@ -0,0 +1,24 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; + +import java.util.UUID; + +public interface OAuth2ParamsRepository extends CrudRepository { +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java new file mode 100644 index 0000000000..676df8adcc --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java @@ -0,0 +1,52 @@ +/** + * Copyright © 2016-2021 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.dao.sql.oauth2; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2RegistrationRepository extends CrudRepository { + + @Query("SELECT reg " + + "FROM OAuth2RegistrationEntity reg " + + "LEFT JOIN OAuth2ParamsEntity params on reg.oauth2ParamsId = params.id " + + "LEFT JOIN OAuth2DomainEntity domain on reg.oauth2ParamsId = domain.oauth2ParamsId " + + "LEFT JOIN OAuth2MobileEntity mobile on reg.oauth2ParamsId = mobile.oauth2ParamsId " + + "WHERE params.enabled = true " + + "AND domain.domainName = :domainName " + + "AND domain.domainScheme IN (:domainSchemes) " + + "AND (:pkgName IS NULL OR mobile.pkgName = :pkgName)") + List findAllEnabledByDomainSchemesNameAndPkgName(@Param("domainSchemes") List domainSchemes, + @Param("domainName") String domainName, + @Param("pkgName") String pkgName); + + List findByOauth2ParamsId(UUID oauth2ParamsId); + + @Query("SELECT mobile.callbackUrlScheme " + + "FROM OAuth2MobileEntity mobile " + + "LEFT JOIN OAuth2RegistrationEntity reg on mobile.oauth2ParamsId = reg.oauth2ParamsId " + + "WHERE reg.id = :registrationId " + + "AND mobile.pkgName = :pkgName") + String findCallbackUrlScheme(@Param("registrationId") UUID id, + @Param("pkgName") String pkgName); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java similarity index 82% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java index 8aea9c9c3f..8dfe92b18d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationDao.java @@ -13,19 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistration; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import java.util.UUID; +@Deprecated @Component @RequiredArgsConstructor public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao implements OAuth2ClientRegistrationDao { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java similarity index 85% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java index 0a25678e16..d37ca981f2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/JpaOAuth2ClientRegistrationInfoDao.java @@ -13,17 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import lombok.RequiredArgsConstructor; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.ExtendedOAuth2ClientRegistrationInfo; +import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationInfoDao; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.oauth2.deprecated.OAuth2ClientRegistrationInfoDao; import org.thingsboard.server.dao.sql.JpaAbstractDao; import java.util.ArrayList; @@ -31,6 +31,7 @@ import java.util.List; import java.util.UUID; import java.util.stream.Collectors; +@Deprecated @Component @RequiredArgsConstructor public class JpaOAuth2ClientRegistrationInfoDao extends JpaAbstractDao implements OAuth2ClientRegistrationInfoDao { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java similarity index 81% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java index 18ac3be05a..73a2ca9bba 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationInfoRepository.java @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; import org.thingsboard.server.common.data.oauth2.SchemeType; -import org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.model.sql.deprecated.ExtendedOAuth2ClientRegistrationInfoEntity; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationInfoEntity; import java.util.List; import java.util.UUID; +@Deprecated public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository { @Query("SELECT new OAuth2ClientRegistrationInfoEntity(cr_info) " + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + @@ -34,7 +35,7 @@ public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository findAllByDomainSchemesAndName(@Param("domainSchemes") List domainSchemes, @Param("domainName") String domainName); - @Query("SELECT new org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + + @Query("SELECT new org.thingsboard.server.dao.model.sql.deprecated.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + "LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId ") List findAllExtended(); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java similarity index 83% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java index 1849f46514..1f4e4783ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/deprecated/OAuth2ClientRegistrationRepository.java @@ -13,12 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.oauth2.deprecated; import org.springframework.data.repository.CrudRepository; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.model.sql.deprecated.OAuth2ClientRegistrationEntity; import java.util.UUID; +@Deprecated public interface OAuth2ClientRegistrationRepository extends CrudRepository { } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 2c5113867a..0e7aec5c19 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -374,9 +374,16 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary ( CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, additional_info varchar, client_id varchar(255), @@ -404,15 +411,28 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( custom_url varchar(255), custom_username varchar(255), custom_password varchar(255), - custom_send_token boolean + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, domain_name varchar(255), domain_scheme varchar(31), - client_registration_info_id uuid + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( @@ -443,6 +463,49 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, + enabled boolean, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean +); + +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + client_registration_info_id uuid +); + CREATE TABLE IF NOT EXISTS api_usage_state ( id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, created_time bigint NOT NULL, diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 3140ff83d6..377d140597 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -411,9 +411,16 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_params ( + id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY, enabled boolean, + tenant_id uuid, + created_time bigint NOT NULL +); + +CREATE TABLE IF NOT EXISTS oauth2_registration ( + id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, additional_info varchar, client_id varchar(255), @@ -441,15 +448,28 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( custom_url varchar(255), custom_username varchar(255), custom_password varchar(255), - custom_send_token boolean + custom_send_token boolean, + CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, +CREATE TABLE IF NOT EXISTS oauth2_domain ( + id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, created_time bigint NOT NULL, domain_name varchar(255), domain_scheme varchar(31), - client_registration_info_id uuid + CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) +); + +CREATE TABLE IF NOT EXISTS oauth2_mobile ( + id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, + oauth2_params_id uuid NOT NULL, + created_time bigint NOT NULL, + pkg_name varchar(255), + callback_url_scheme varchar(255), + CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, + CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) ); CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( @@ -480,6 +500,49 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, + enabled boolean, + created_time bigint NOT NULL, + additional_info varchar, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + scope varchar(255), + user_info_uri varchar(255), + user_name_attribute_name varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), + allow_user_creation boolean, + activate_user boolean, + type varchar(31), + basic_email_attribute_key varchar(31), + basic_first_name_attribute_key varchar(31), + basic_last_name_attribute_key varchar(31), + basic_tenant_name_strategy varchar(31), + basic_tenant_name_pattern varchar(255), + basic_customer_name_pattern varchar(255), + basic_default_dashboard_name varchar(255), + basic_always_full_screen boolean, + custom_url varchar(255), + custom_username varchar(255), + custom_password varchar(255), + custom_send_token boolean +); + +-- Deprecated +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + domain_name varchar(255), + domain_scheme varchar(31), + client_registration_info_id uuid +); + CREATE TABLE IF NOT EXISTS api_usage_state ( id uuid NOT NULL CONSTRAINT usage_record_pkey PRIMARY KEY, created_time bigint NOT NULL, diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index ebfbfccd2a..fa79e32bea 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -16,208 +16,225 @@ package org.thingsboard.server.dao.service; import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; public class BaseOAuth2ServiceTest extends AbstractServiceTest { - private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new ArrayList<>()); + private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, Collections.emptyList()); @Autowired protected OAuth2Service oAuth2Service; @Before public void beforeRun() { - Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); + Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); } @After public void after() { - oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); - Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); - Assert.assertTrue(oAuth2Service.findOAuth2Params().getDomainsParams().isEmpty()); + oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); + Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); + Assert.assertTrue(oAuth2Service.findOAuth2Info().getOauth2ParamsInfos().isEmpty()); } @Test(expected = DataValidationException.class) public void testSaveHttpAndMixedDomainsTogether() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); } @Test(expected = DataValidationException.class) public void testSaveHttpsAndMixedDomainsTogether() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); } @Test public void testCreateAndFindParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); // TODO ask if it's safe to check equality on AdditionalProperties - Assert.assertEquals(clientsParams, foundClientsParams); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); } @Test public void testDisableParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - clientsParams.setEnabled(true); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - clientsParams.setEnabled(false); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundDisabledClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertEquals(clientsParams, foundDisabledClientsParams); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Info.setEnabled(true); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + oAuth2Info.setEnabled(false); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundDisabledOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertEquals(oAuth2Info, foundDisabledOAuth2Info); } @Test public void testClearDomainParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); - OAuth2ClientsParams foundAfterClearClientsParams = oAuth2Service.findOAuth2Params(); + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); + OAuth2Info foundAfterClearClientsParams = oAuth2Service.findOAuth2Info(); Assert.assertNotNull(foundAfterClearClientsParams); Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); } @Test public void testUpdateClientsParams() { - OAuth2ClientsParams clientsParams = createDefaultClientsParams(); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); - - OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = createDefaultOAuth2Info(); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + OAuth2Info newOAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(newClientsParams); - OAuth2ClientsParams foundAfterUpdateClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundAfterUpdateClientsParams); - Assert.assertEquals(newClientsParams, foundAfterUpdateClientsParams); + oAuth2Service.saveOAuth2Info(newOAuth2Info); + OAuth2Info foundAfterUpdateOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundAfterUpdateOAuth2Info); + Assert.assertEquals(newOAuth2Info, foundAfterUpdateOAuth2Info); } @Test public void testGetOAuth2Clients() { - List firstGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + List firstGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() ); - List secondGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + List secondGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo() ); - List thirdGroup = Lists.newArrayList( - validClientRegistrationDto() + List thirdGroup = Lists.newArrayList( + validRegistrationInfo() ); - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(firstGroup) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(secondGroup) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(thirdGroup) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); List firstGroupClientInfos = firstGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); List secondGroupClientInfos = secondGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); List thirdGroupClientInfos = thirdGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain"); + List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null); Assert.assertTrue(nonExistentDomainClients.isEmpty()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -227,10 +244,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); Assert.assertTrue(firstDomainHttpsClients.isEmpty()); - List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain"); + List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -239,7 +256,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { && clientInfo.getName().equals(secondGroupClientInfo.getName())) ); }); - List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain"); + List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null); Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( @@ -249,7 +266,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -266,7 +283,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain"); + List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null); Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -286,34 +303,35 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetOAuth2ClientsForHttpAndHttps() { - List firstGroup = Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + List firstGroup = Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() ); - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(firstGroup) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); - Assert.assertNotNull(foundClientsParams); - Assert.assertEquals(clientsParams, foundClientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertNotNull(foundOAuth2Info); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); List firstGroupClientInfos = firstGroup.stream() - .map(clientRegistrationDto -> new OAuth2ClientInfo( - clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .map(registrationInfo -> new OAuth2ClientInfo( + registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) .collect(Collectors.toList()); - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -323,7 +341,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null); Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( @@ -336,166 +354,220 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetDisabledOAuth2Clients() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(5, secondDomainHttpClients.size()); - clientsParams.setEnabled(false); - oAuth2Service.saveOAuth2Params(clientsParams); + oAuth2Info.setEnabled(false); + oAuth2Service.saveOAuth2Info(oAuth2Info); - List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); + List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null); Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); } @Test - public void testFindAllClientRegistrationInfos() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + public void testFindAllRegistrations() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - List foundClientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); - Assert.assertEquals(6, foundClientRegistrationInfos.size()); - clientsParams.getDomainsParams().stream() - .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) - .forEach(clientRegistrationDto -> + oAuth2Service.saveOAuth2Info(oAuth2Info); + List foundRegistrations = oAuth2Service.findAllRegistrations(); + Assert.assertEquals(6, foundRegistrations.size()); + oAuth2Info.getOauth2ParamsInfos().stream() + .flatMap(paramsInfo -> paramsInfo.getClientRegistrations().stream()) + .forEach(registrationInfo -> Assert.assertTrue( - foundClientRegistrationInfos.stream() - .anyMatch(clientRegistrationInfo -> clientRegistrationInfo.getClientId().equals(clientRegistrationDto.getClientId())) + foundRegistrations.stream() + .anyMatch(registration -> registration.getClientId().equals(registrationInfo.getClientId())) ) ); } @Test - public void testFindClientRegistrationById() { - OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + public void testFindRegistrationById() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), + OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() )) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto() + validRegistrationInfo() )) .build() )); - oAuth2Service.saveOAuth2Params(clientsParams); - List clientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); - clientRegistrationInfos.forEach(clientRegistrationInfo -> { - OAuth2ClientRegistrationInfo foundClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(clientRegistrationInfo.getUuidId()); - Assert.assertEquals(clientRegistrationInfo, foundClientRegistrationInfo); + oAuth2Service.saveOAuth2Info(oAuth2Info); + List foundRegistrations = oAuth2Service.findAllRegistrations(); + foundRegistrations.forEach(registration -> { + OAuth2Registration foundRegistration = oAuth2Service.findRegistration(registration.getUuidId()); + Assert.assertEquals(registration, foundRegistration); }); } - private OAuth2ClientsParams createDefaultClientsParams() { - return new OAuth2ClientsParams(true, Lists.newArrayList( - OAuth2ClientsDomainParams.builder() + @Test + public void testFindCallbackUrlScheme() { + OAuth2Info oAuth2Info = new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + )) + .mobileInfos(Lists.newArrayList( + OAuth2MobileInfo.builder().pkgName("com.test.pkg1").callbackUrlScheme("testPkg1Callback").build(), + OAuth2MobileInfo.builder().pkgName("com.test.pkg2").callbackUrlScheme("testPkg2Callback").build() + )) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() + )) + .build(), + OAuth2ParamsInfo.builder() + .domainInfos(Lists.newArrayList( + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + )) + .mobileInfos(Collections.emptyList()) + .clientRegistrations(Lists.newArrayList( + validRegistrationInfo(), + validRegistrationInfo() + )) + .build() + )); + oAuth2Service.saveOAuth2Info(oAuth2Info); + + OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); + Assert.assertEquals(oAuth2Info, foundOAuth2Info); + + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1"); + Assert.assertEquals(3, firstDomainHttpClients.size()); + for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { + String[] segments = clientInfo.getUrl().split("/"); + String registrationId = segments[segments.length-1]; + String callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg1"); + Assert.assertNotNull(callbackUrlScheme); + Assert.assertEquals("testPkg1Callback", callbackUrlScheme); + callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg2"); + Assert.assertNotNull(callbackUrlScheme); + Assert.assertEquals("testPkg2Callback", callbackUrlScheme); + callbackUrlScheme = oAuth2Service.findCallbackUrlScheme(UUID.fromString(registrationId), "com.test.pkg3"); + Assert.assertNull(callbackUrlScheme); + } + } + + private OAuth2Info createDefaultOAuth2Info() { + return new OAuth2Info(true, Lists.newArrayList( + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo(), + validRegistrationInfo() )) .build(), - OAuth2ClientsDomainParams.builder() + OAuth2ParamsInfo.builder() .domainInfos(Lists.newArrayList( - DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() + OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() )) + .mobileInfos(Collections.emptyList()) .clientRegistrations(Lists.newArrayList( - validClientRegistrationDto(), - validClientRegistrationDto() + validRegistrationInfo(), + validRegistrationInfo() )) .build() )); } - private ClientRegistrationDto validClientRegistrationDto() { - return ClientRegistrationDto.builder() + private OAuth2RegistrationInfo validRegistrationInfo() { + return OAuth2RegistrationInfo.builder() .clientId(UUID.randomUUID().toString()) .clientSecret(UUID.randomUUID().toString()) .authorizationUri(UUID.randomUUID().toString()) diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index 726b4ba412..f7b1b4118d 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -24,9 +24,13 @@ DROP TABLE IF EXISTS dashboard; DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; +DROP TABLE IF EXISTS oauth2_mobile; +DROP TABLE IF EXISTS oauth2_domain; +DROP TABLE IF EXISTS oauth2_registration; +DROP TABLE IF EXISTS oauth2_params; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; -DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS ota_package; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 855a53df2d..a29dea43c2 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -25,9 +25,13 @@ DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS tb_schema_settings; +DROP TABLE IF EXISTS oauth2_mobile; +DROP TABLE IF EXISTS oauth2_domain; +DROP TABLE IF EXISTS oauth2_registration; +DROP TABLE IF EXISTS oauth2_params; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; -DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS api_usage_state; DROP TABLE IF EXISTS resource; DROP TABLE IF EXISTS firmware; diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 8ad95805a5..14a008634b 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -103,7 +103,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.page.PageData; @@ -142,7 +142,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -1773,21 +1772,28 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable { }).getBody(); } - public List getOAuth2Clients() { + public List getOAuth2Clients(String pkgName) { + Map params = new HashMap<>(); + StringBuilder urlBuilder = new StringBuilder(baseURL); + urlBuilder.append("/api/noauth/oauth2Clients"); + if (pkgName != null) { + urlBuilder.append("?pkgName={pkgName}"); + params.put("pkgName", pkgName); + } return restTemplate.exchange( - baseURL + "/api/noauth/oauth2Clients", + urlBuilder.toString(), HttpMethod.POST, HttpEntity.EMPTY, new ParameterizedTypeReference>() { - }).getBody(); + }, params).getBody(); } - public OAuth2ClientsParams getCurrentOAuth2Params() { - return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2ClientsParams.class).getBody(); + public OAuth2Info getCurrentOAuth2Info() { + return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2Info.class).getBody(); } - public OAuth2ClientsParams saveOAuth2Params(OAuth2ClientsParams oauth2Params) { - return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Params, OAuth2ClientsParams.class).getBody(); + public OAuth2Info saveOAuth2Info(OAuth2Info oauth2Info) { + return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Info, OAuth2Info.class).getBody(); } public String getLoginProcessingUrl() { diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index c17d993bf7..68724fa0dc 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -45,7 +45,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { AlertDialogComponent } from '@shared/components/dialog/alert-dialog.component'; import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; -import { isMobileApp } from '@core/utils'; +import { isDefinedAndNotNull, isMobileApp } from '@core/utils'; @Injectable({ providedIn: 'root' @@ -204,8 +204,12 @@ export class AuthService { } } - public loadOAuth2Clients(): Observable> { - return this.http.post>(`/api/noauth/oauth2Clients`, + public loadOAuth2Clients(pkgName?: string): Observable> { + let url = '/api/noauth/oauth2Clients'; + if (isDefinedAndNotNull(pkgName)) { + url += `?pkgName=${pkgName}`; + } + return this.http.post>(url, null, defaultHttpOptions()).pipe( catchError(err => of([])), tap((OAuth2Clients) => { diff --git a/ui-ngx/src/app/core/http/oauth2.service.ts b/ui-ngx/src/app/core/http/oauth2.service.ts index e81d472130..72a6bc77af 100644 --- a/ui-ngx/src/app/core/http/oauth2.service.ts +++ b/ui-ngx/src/app/core/http/oauth2.service.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { Observable } from 'rxjs'; -import { OAuth2ClientRegistrationTemplate, OAuth2ClientsParams } from '@shared/models/oauth2.models'; +import { OAuth2ClientRegistrationTemplate, OAuth2Info } from '@shared/models/oauth2.models'; @Injectable({ providedIn: 'root' @@ -29,16 +29,16 @@ export class OAuth2Service { private http: HttpClient ) { } - public getOAuth2Settings(config?: RequestConfig): Observable { - return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); + public getOAuth2Settings(config?: RequestConfig): Observable { + return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); } public getOAuth2Template(config?: RequestConfig): Observable> { return this.http.get>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); } - public saveOAuth2Settings(OAuth2Setting: OAuth2ClientsParams, config?: RequestConfig): Observable { - return this.http.post('/api/oauth2/config', OAuth2Setting, + public saveOAuth2Settings(OAuth2Setting: OAuth2Info, config?: RequestConfig): Observable { + return this.http.post('/api/oauth2/config', OAuth2Setting, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 3998b4fc9a..3ea9a1ca11 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -34,19 +34,19 @@ {{ 'admin.oauth2.enable' | translate }}
- +
- + - {{ domainListTittle(domain) }} + {{ domainListTittle(oauth2ParamsInfo) }} + + + + + + + +
- - {{ 'admin.domain-name-unique' | translate }} - -
-
- - admin.oauth2.redirect-uri-template - - - - - - - -
+
+
+ +
+ + + + +
+
+ admin.oauth2.no-mobile-apps +
+
+
+
+
+ + admin.oauth2.mobile-package + + + + {{ 'admin.oauth2.mobile-package-unique' | translate }} + +
+ + admin.oauth2.mobile-callback-url-scheme + + +
+
+ - +
-
- -
-
+
+
-
-
-
- -
- - + + + +
admin.oauth2.providers
- @@ -140,8 +190,8 @@ @@ -467,7 +517,7 @@ diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index e17d7c34e4..aeb401dedf 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -62,7 +62,7 @@ } } } - .domains-list{ + .domains-list, .apps-list { margin-bottom: 1.5em; .mat-form-field-suffix .mat-icon-button .mat-icon{ font-size: 24px; diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index e93ea168fe..39fb6ac6ff 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -18,8 +18,6 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms'; import { ClientAuthenticationMethod, - ClientRegistration, - DomainInfo, DomainSchema, domainSchemaTranslations, MapperConfig, @@ -27,8 +25,10 @@ import { MapperConfigCustom, MapperConfigType, OAuth2ClientRegistrationTemplate, - OAuth2ClientsDomainParams, - OAuth2ClientsParams, + OAuth2DomainInfo, + OAuth2Info, OAuth2MobileInfo, + OAuth2ParamsInfo, + OAuth2RegistrationInfo, TenantNameStrategy } from '@shared/models/oauth2.models'; import { Store } from '@ngrx/store'; @@ -52,6 +52,20 @@ import { ActivatedRoute } from '@angular/router'; }) export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { + constructor(protected store: Store, + private route: ActivatedRoute, + private oauth2Service: OAuth2Service, + private fb: FormBuilder, + private dialogService: DialogService, + private translate: TranslateService, + @Inject(WINDOW) private window: Window) { + super(store); + } + + get oauth2ParamsInfos(): FormArray { + return this.oauth2SettingsForm.get('oauth2ParamsInfos') as FormArray; + } + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; private DOMAIN_AND_PORT_REGEXP = /^(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?$/; private subscriptions: Subscription[] = []; @@ -77,7 +91,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha readonly separatorKeysCodes: number[] = [ENTER, COMMA]; oauth2SettingsForm: FormGroup; - auth2ClientsParams: OAuth2ClientsParams; + oauth2Info: OAuth2Info; clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); mapperConfigType = MapperConfigType; @@ -90,14 +104,14 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private loginProcessingUrl: string = this.route.snapshot.data.loginProcessingUrl; - constructor(protected store: Store, - private route: ActivatedRoute, - private oauth2Service: OAuth2Service, - private fb: FormBuilder, - private dialogService: DialogService, - private translate: TranslateService, - @Inject(WINDOW) private window: Window) { - super(store); + private static validateScope(control: AbstractControl): ValidationErrors | null { + const scope: string[] = control.value; + if (!scope || !scope.length) { + return { + required: true + }; + } + return null; } ngOnInit(): void { @@ -106,10 +120,10 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.oauth2Service.getOAuth2Template(), this.oauth2Service.getOAuth2Settings() ]).subscribe( - ([templates, auth2ClientsParams]) => { + ([templates, oauth2Info]) => { this.initTemplates(templates); - this.auth2ClientsParams = auth2ClientsParams; - this.initOAuth2Settings(this.auth2ClientsParams); + this.oauth2Info = oauth2Info; + this.initOAuth2Settings(this.oauth2Info); } ); } @@ -130,11 +144,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.templateProvider.sort(); } - get domainsParams(): FormArray { - return this.oauth2SettingsForm.get('domainsParams') as FormArray; - } - - private formBasicGroup(type: MapperConfigType, mapperConfigBasic?: MapperConfigBasic): FormGroup { + private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { let tenantNamePattern; if (mapperConfigBasic?.tenantNamePattern) { tenantNamePattern = mapperConfigBasic.tenantNamePattern; @@ -173,16 +183,16 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ - domainsParams: this.fb.array([]), + oauth2ParamsInfos: this.fb.array([]), enabled: [false] }); } - private initOAuth2Settings(auth2ClientsParams: OAuth2ClientsParams): void { - if (auth2ClientsParams) { - this.oauth2SettingsForm.patchValue({enabled: auth2ClientsParams.enabled}, {emitEvent: false}); - auth2ClientsParams.domainsParams.forEach((domain) => { - this.domainsParams.push(this.buildDomainsForm(domain)); + private initOAuth2Settings(oauth2Info: OAuth2Info): void { + if (oauth2Info) { + this.oauth2SettingsForm.patchValue({enabled: oauth2Info.enabled}, {emitEvent: false}); + oauth2Info.oauth2ParamsInfos.forEach((oauth2ParamsInfo) => { + this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm(oauth2ParamsInfo)); }); } } @@ -201,8 +211,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return null; } + private uniquePkgNameValidator(control: FormGroup): { [key: string]: boolean } | null { + if (control.parent?.value) { + const pkgName = control.value.pkgName; + const mobileInfosList = control.parent.getRawValue() + .filter((mobileInfo) => mobileInfo.pkgName === pkgName); + if (mobileInfosList.length > 1) { + return {unique: true}; + } + } + return null; + } + public domainListTittle(control: AbstractControl): string { - const domainInfos = control.get('domainInfos').value as DomainInfo[]; + const domainInfos = control.get('domainInfos').value as OAuth2DomainInfo[]; if (domainInfos.length) { const domainList = new Set(); domainInfos.forEach((domain) => { @@ -213,41 +235,51 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return this.translate.instant('admin.oauth2.new-domain'); } - private buildDomainsForm(auth2ClientsDomainParams?: OAuth2ClientsDomainParams): FormGroup { - const formDomain = this.fb.group({ + private buildOAuth2ParamsInfoForm(oauth2ParamsInfo?: OAuth2ParamsInfo): FormGroup { + const formOAuth2Params = this.fb.group({ domainInfos: this.fb.array([], Validators.required), + mobileInfos: this.fb.array([]), clientRegistrations: this.fb.array([], Validators.required) }); - if (auth2ClientsDomainParams) { - auth2ClientsDomainParams.domainInfos.forEach((domain) => { - this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); + if (oauth2ParamsInfo) { + oauth2ParamsInfo.domainInfos.forEach((domain) => { + this.domainInfos(formOAuth2Params).push(this.buildDomainInfoForm(domain)); + }); + oauth2ParamsInfo.mobileInfos.forEach((mobile) => { + this.mobileInfos(formOAuth2Params).push(this.buildMobileInfoForm(mobile)); }); - auth2ClientsDomainParams.clientRegistrations.forEach((registration) => { - this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); + oauth2ParamsInfo.clientRegistrations.forEach((registration) => { + this.clientRegistrations(formOAuth2Params).push(this.buildRegistrationForm(registration)); }); } else { - this.clientDomainProviders(formDomain).push(this.buildProviderForm()); - this.clientDomainInfos(formDomain).push(this.buildDomainForm()); + this.clientRegistrations(formOAuth2Params).push(this.buildRegistrationForm()); + this.domainInfos(formOAuth2Params).push(this.buildDomainInfoForm()); } - return formDomain; + return formOAuth2Params; } - private buildDomainForm(domainInfo?: DomainInfo): FormGroup { - const domain = this.fb.group({ + private buildDomainInfoForm(domainInfo?: OAuth2DomainInfo): FormGroup { + return this.fb.group({ name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ Validators.required, Validators.pattern(this.DOMAIN_AND_PORT_REGEXP)]], scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] }, {validators: this.uniqueDomainValidator}); - return domain; } - private buildProviderForm(clientRegistration?: ClientRegistration): FormGroup { + private buildMobileInfoForm(mobileInfo?: OAuth2MobileInfo): FormGroup { + return this.fb.group({ + pkgName: [mobileInfo?.pkgName, [Validators.required]], + callbackUrlScheme: [mobileInfo?.callbackUrlScheme, [Validators.required]], + }, {validators: this.uniquePkgNameValidator}); + } + + private buildRegistrationForm(registration?: OAuth2RegistrationInfo): FormGroup { let additionalInfo = null; - if (isDefinedAndNotNull(clientRegistration?.additionalInfo)) { - additionalInfo = clientRegistration.additionalInfo; + if (isDefinedAndNotNull(registration?.additionalInfo)) { + additionalInfo = registration.additionalInfo; if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { additionalInfo.providerName = 'Custom'; } @@ -261,43 +293,43 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: this.fb.group({ providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] }), - loginButtonLabel: [clientRegistration?.loginButtonLabel ? clientRegistration.loginButtonLabel : null, Validators.required], - loginButtonIcon: [clientRegistration?.loginButtonIcon ? clientRegistration.loginButtonIcon : null], - clientId: [clientRegistration?.clientId ? clientRegistration.clientId : '', Validators.required], - clientSecret: [clientRegistration?.clientSecret ? clientRegistration.clientSecret : '', Validators.required], - accessTokenUri: [clientRegistration?.accessTokenUri ? clientRegistration.accessTokenUri : '', + loginButtonLabel: [registration?.loginButtonLabel ? registration.loginButtonLabel : null, Validators.required], + loginButtonIcon: [registration?.loginButtonIcon ? registration.loginButtonIcon : null], + clientId: [registration?.clientId ? registration.clientId : '', Validators.required], + clientSecret: [registration?.clientSecret ? registration.clientSecret : '', Validators.required], + accessTokenUri: [registration?.accessTokenUri ? registration.accessTokenUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - authorizationUri: [clientRegistration?.authorizationUri ? clientRegistration.authorizationUri : '', + authorizationUri: [registration?.authorizationUri ? registration.authorizationUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - scope: this.fb.array(clientRegistration?.scope ? clientRegistration.scope : [], this.validateScope), - jwkSetUri: [clientRegistration?.jwkSetUri ? clientRegistration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], - userInfoUri: [clientRegistration?.userInfoUri ? clientRegistration.userInfoUri : '', + scope: this.fb.array(registration?.scope ? registration.scope : [], OAuth2SettingsComponent.validateScope), + jwkSetUri: [registration?.jwkSetUri ? registration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], + userInfoUri: [registration?.userInfoUri ? registration.userInfoUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: [ - clientRegistration?.clientAuthenticationMethod ? clientRegistration.clientAuthenticationMethod : ClientAuthenticationMethod.POST, + registration?.clientAuthenticationMethod ? registration.clientAuthenticationMethod : ClientAuthenticationMethod.POST, Validators.required], userNameAttributeName: [ - clientRegistration?.userNameAttributeName ? clientRegistration.userNameAttributeName : 'email', Validators.required], + registration?.userNameAttributeName ? registration.userNameAttributeName : 'email', Validators.required], mapperConfig: this.fb.group({ allowUserCreation: [ - isDefinedAndNotNull(clientRegistration?.mapperConfig?.allowUserCreation) ? - clientRegistration.mapperConfig.allowUserCreation : true + isDefinedAndNotNull(registration?.mapperConfig?.allowUserCreation) ? + registration.mapperConfig.allowUserCreation : true ], activateUser: [ - isDefinedAndNotNull(clientRegistration?.mapperConfig?.activateUser) ? clientRegistration.mapperConfig.activateUser : false + isDefinedAndNotNull(registration?.mapperConfig?.activateUser) ? registration.mapperConfig.activateUser : false ], type: [ - clientRegistration?.mapperConfig?.type ? clientRegistration.mapperConfig.type : MapperConfigType.BASIC, Validators.required + registration?.mapperConfig?.type ? registration.mapperConfig.type : MapperConfigType.BASIC, Validators.required ] } ) }); - if (clientRegistration) { - this.changeMapperConfigType(clientRegistrationFormGroup, clientRegistration.mapperConfig.type, clientRegistration.mapperConfig); + if (registration) { + this.changeMapperConfigType(clientRegistrationFormGroup, registration.mapperConfig.type, registration.mapperConfig); } else { this.changeMapperConfigType(clientRegistrationFormGroup, MapperConfigType.BASIC); this.setProviderDefaultValue(defaultProviderName, clientRegistrationFormGroup); @@ -315,16 +347,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return clientRegistrationFormGroup; } - private validateScope(control: AbstractControl): ValidationErrors | null { - const scope: string[] = control.value; - if (!scope || !scope.length) { - return { - required: true - }; - } - return null; - } - private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { if (provider === 'Custom') { clientRegistration.reset(this.defaultProvider, {emitEvent: false}); @@ -355,7 +377,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } else { mapperConfig.removeControl('custom'); if (!mapperConfig.get('basic')) { - mapperConfig.addControl('basic', this.formBasicGroup(type, predefinedValue?.basic)); + mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); } if (type === MapperConfigType.GITHUB) { mapperConfig.get('basic.emailAttributeKey').disable(); @@ -370,7 +392,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const setting = this.oauth2SettingsForm.getRawValue(); this.oauth2Service.saveOAuth2Settings(setting).subscribe( (oauth2Settings) => { - this.auth2ClientsParams = oauth2Settings; + this.oauth2Info = oauth2Settings; this.oauth2SettingsForm.patchValue(this.oauth2SettingsForm, {emitEvent: false}); this.oauth2SettingsForm.markAsUntouched(); this.oauth2SettingsForm.markAsPristine(); @@ -403,58 +425,62 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha controller.markAsDirty(); } - addDomain(): void { - this.domainsParams.push(this.buildDomainsForm()); + addOAuth2ParamsInfo(): void { + this.oauth2ParamsInfos.push(this.buildOAuth2ParamsInfoForm()); } - deleteDomain($event: Event, index: number): void { + deleteOAuth2ParamsInfo($event: Event, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const domainName = this.domainListTittle(this.domainsParams.at(index)); + const domainName = this.domainListTittle(this.oauth2ParamsInfos.at(index)); this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.domainsParams.removeAt(index); - this.domainsParams.markAsTouched(); - this.domainsParams.markAsDirty(); + this.oauth2ParamsInfos.removeAt(index); + this.oauth2ParamsInfos.markAsTouched(); + this.oauth2ParamsInfos.markAsDirty(); } }); } - clientDomainProviders(control: AbstractControl): FormArray { + clientRegistrations(control: AbstractControl): FormArray { return control.get('clientRegistrations') as FormArray; } - clientDomainInfos(control: AbstractControl): FormArray { + domainInfos(control: AbstractControl): FormArray { return control.get('domainInfos') as FormArray; } - addProvider(control: AbstractControl): void { - this.clientDomainProviders(control).push(this.buildProviderForm()); + mobileInfos(control: AbstractControl): FormArray { + return control.get('mobileInfos') as FormArray; } - deleteProvider($event: Event, control: AbstractControl, index: number): void { + addRegistration(control: AbstractControl): void { + this.clientRegistrations(control).push(this.buildRegistrationForm()); + } + + deleteRegistration($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value; + const providerName = this.clientRegistrations(control).at(index).get('additionalInfo.providerName').value; this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.clientDomainProviders(control).removeAt(index); - this.clientDomainProviders(control).markAsTouched(); - this.clientDomainProviders(control).markAsDirty(); + this.clientRegistrations(control).removeAt(index); + this.clientRegistrations(control).markAsTouched(); + this.clientRegistrations(control).markAsDirty(); } }); } @@ -480,24 +506,41 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } addDomainInfo(control: AbstractControl): void { - this.clientDomainInfos(control).push(this.buildDomainForm({ + this.domainInfos(control).push(this.buildDomainInfoForm({ name: '', scheme: DomainSchema.HTTPS })); } - removeDomain($event: Event, control: AbstractControl, index: number): void { + removeDomainInfo($event: Event, control: AbstractControl, index: number): void { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + this.domainInfos(control).removeAt(index); + this.domainInfos(control).markAsTouched(); + this.domainInfos(control).markAsDirty(); + } + + addMobileInfo(control: AbstractControl): void { + this.mobileInfos(control).push(this.buildMobileInfoForm({ + pkgName: '', + callbackUrlScheme: '' + })); + } + + removeMobileInfo($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - this.clientDomainInfos(control).removeAt(index); - this.clientDomainInfos(control).markAsTouched(); - this.clientDomainInfos(control).markAsDirty(); + this.mobileInfos(control).removeAt(index); + this.mobileInfos(control).markAsTouched(); + this.mobileInfos(control).markAsDirty(); } redirectURI(control: AbstractControl, schema?: DomainSchema): string { - const domainInfo = control.value as DomainInfo; + const domainInfo = control.value as OAuth2DomainInfo; if (domainInfo.name !== '') { let protocol; if (isDefined(schema)) { diff --git a/ui-ngx/src/app/shared/models/oauth2.models.ts b/ui-ngx/src/app/shared/models/oauth2.models.ts index 08ff402502..93b147c887 100644 --- a/ui-ngx/src/app/shared/models/oauth2.models.ts +++ b/ui-ngx/src/app/shared/models/oauth2.models.ts @@ -16,21 +16,27 @@ import { HasUUID } from '@shared/models/id/has-uuid'; -export interface OAuth2ClientsParams { +export interface OAuth2Info { enabled: boolean; - domainsParams: OAuth2ClientsDomainParams[]; + oauth2ParamsInfos: OAuth2ParamsInfo[]; } -export interface OAuth2ClientsDomainParams { - clientRegistrations: ClientRegistration[]; - domainInfos: DomainInfo[]; +export interface OAuth2ParamsInfo { + clientRegistrations: OAuth2RegistrationInfo[]; + domainInfos: OAuth2DomainInfo[]; + mobileInfos: OAuth2MobileInfo[]; } -export interface DomainInfo { +export interface OAuth2DomainInfo { name: string; scheme: DomainSchema; } +export interface OAuth2MobileInfo { + pkgName: string; + callbackUrlScheme: string; +} + export enum DomainSchema{ HTTP = 'HTTP', HTTPS = 'HTTPS', @@ -57,7 +63,7 @@ export enum TenantNameStrategy{ CUSTOM = 'CUSTOM' } -export interface OAuth2ClientRegistrationTemplate extends ClientRegistration{ +export interface OAuth2ClientRegistrationTemplate extends OAuth2RegistrationInfo{ comment: string; createdTime: number; helpLink: string; @@ -66,7 +72,7 @@ export interface OAuth2ClientRegistrationTemplate extends ClientRegistration{ id: HasUUID; } -export interface ClientRegistration { +export interface OAuth2RegistrationInfo { loginButtonLabel: string; loginButtonIcon: string; clientId: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index aabd4207d8..5fbb6ed3fb 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -219,7 +219,16 @@ "domain-schema-http": "HTTP", "domain-schema-https": "HTTPS", "domain-schema-mixed": "HTTP+HTTPS", - "enable": "Enable OAuth2 settings" + "enable": "Enable OAuth2 settings", + "domains": "Domains", + "mobile-apps": "Mobile applications", + "no-mobile-apps": "No applications configured", + "mobile-package": "Application package", + "mobile-package-unique": "Application package must be unique.", + "mobile-callback-url-scheme": "Callback URL scheme", + "add-mobile-app": "Add application", + "delete-mobile-app": "Delete application info", + "providers": "Providers" } }, "alarm": { From 308650a308aa583dffc44d37cb1930ac747560e7 Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Wed, 9 Jun 2021 19:21:40 +0300 Subject: [PATCH 74/75] LWM2M: front add coap resource URI --- .../transport/coap/CoapTransportResource.java | 21 ++- .../DefaultLwM2MTransportMsgHandler.java | 8 +- .../server/LwM2mTransportCoapResource.java | 38 +++--- .../server/client/LwM2mClientContextImpl.java | 16 ++- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 11 +- ...ile-transport-configuration.component.html | 128 +++++++++++++----- ...ofile-transport-configuration.component.ts | 80 ++++++++++- .../lwm2m/lwm2m-profile-config.models.ts | 15 +- .../assets/locale/locale.constant-en_US.json | 5 +- 9 files changed, 246 insertions(+), 76 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index cd80aa42df..a1a2a4b656 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -398,7 +398,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private Optional decodeCredentials(Request request) { List uriPath = request.getOptions().getUriPath(); - if (uriPath.size() > ACCESS_TOKEN_POSITION) { + if (uriPath.size() >= ACCESS_TOKEN_POSITION) { return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); } else { return Optional.empty(); @@ -482,13 +482,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { String title = exchange.getQueryParameter("title"); String version = exchange.getQueryParameter("version"); if (msg.getResponseStatus().equals(TransportProtos.ResponseStatus.SUCCESS)) { + String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); if (msg.getTitle().equals(title) && msg.getVersion().equals(version)) { - String firmwareId = new UUID(msg.getOtaPackageIdMSB(), msg.getOtaPackageIdLSB()).toString(); String strChunkSize = exchange.getQueryParameter("size"); String strChunk = exchange.getQueryParameter("chunk"); int chunkSize = StringUtils.isEmpty(strChunkSize) ? 0 : Integer.parseInt(strChunkSize); int chunk = StringUtils.isEmpty(strChunk) ? 0 : Integer.parseInt(strChunk); exchange.respond(CoAP.ResponseCode.CONTENT, transportContext.getOtaPackageDataCache().get(firmwareId, chunkSize, chunk)); + } + else if (firmwareId != null) { + sendOtaData(exchange, firmwareId); } else { exchange.respond(CoAP.ResponseCode.BAD_REQUEST); } @@ -504,6 +507,20 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } + private void sendOtaData(CoapExchange exchange, String firmwareId) { + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = transportContext.getOtaPackageDataCache().get(firmwareId); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); + boolean moreFlag = fwData.length > chunkSize; + response.getOptions().setBlock2(chunkSize, moreFlag, 0); + } + exchange.respond(response); + } + } + private static class CoapSessionListener implements SessionMsgListener { private final CoapTransportResource coapTransportResource; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java index 4f97273cb6..bff3b9d60d 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java @@ -41,10 +41,10 @@ import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.server.cache.ota.OtaPackageDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.ota.OtaPackageKey; import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageUtil; -import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; import org.thingsboard.server.common.transport.adaptor.AdaptorException; @@ -89,11 +89,9 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; @@ -267,8 +265,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler clientContext.unregister(client, registration); SessionInfoProto sessionInfo = client.getSession(); if (sessionInfo != null) { - transportService.deregisterSession(sessionInfo); this.doCloseSession(sessionInfo); + transportService.deregisterSession(sessionInfo); sessionStore.remove(registration.getEndpoint()); log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java index 4e45b172cb..1236c8b6d2 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportCoapResource.java @@ -69,10 +69,10 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { @Override protected void processHandleGet(CoapExchange exchange) { log.warn("90) processHandleGet [{}]", exchange); - if (exchange.getRequestOptions().getUriPath().size() == 2 && - (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(0)) || - SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(0)))) { - this.sentOtaData(exchange); + if (exchange.getRequestOptions().getUriPath().size() >= 2 && + (FIRMWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)) || + SOFTWARE_UPDATE_COAP_RECOURSE.equals(exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-2)))) { + this.sendOtaData(exchange); } } @@ -129,23 +129,25 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { } } - private void sentOtaData(CoapExchange exchange) { - String idStr = exchange.getRequestOptions().getUriPath().get(1); + private void sendOtaData(CoapExchange exchange) { + String idStr = exchange.getRequestOptions().getUriPath().get(exchange.getRequestOptions().getUriPath().size()-1 + ); UUID currentId = UUID.fromString(idStr); - if (exchange.getRequestOptions().getBlock2() != null) { - int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); - int chunk = 0; - Response response = new Response(CoAP.ResponseCode.CONTENT); - byte[] fwData = this.getOtaData(currentId); - log.warn("91) read softWare data (length): [{}]", fwData.length); - if (fwData != null && fwData.length > 0) { - response.setPayload(fwData); + Response response = new Response(CoAP.ResponseCode.CONTENT); + byte[] fwData = this.getOtaData(currentId); + log.warn("91) read softWare data (length): [{}]", fwData.length); + if (fwData != null && fwData.length > 0) { + response.setPayload(fwData); + if (exchange.getRequestOptions().getBlock2() != null) { + int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); boolean moreFlag = fwData.length > chunkSize; - response.getOptions().setBlock2(chunkSize, moreFlag, chunk); - log.warn("92) Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); - exchange.respond(response); + response.getOptions().setBlock2(chunkSize, moreFlag, 0); + log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); } - + else { + log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length); + } + exchange.respond(response); } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java index c088ac8477..02d3425ede 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientContextImpl.java @@ -36,6 +36,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Predicate; import static org.eclipse.leshan.core.SecurityMode.NO_SEC; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -137,14 +138,19 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { @Override public LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo) { - LwM2mClient lwM2mClient = lwM2mClientsByEndpoint.values().stream().filter(c -> + LwM2mClient lwM2mClient = null; + Predicate isClientFilter = c -> (new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())) - .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))) - - ).findAny().orElse(null); + .equals((new UUID(c.getSession().getSessionIdMSB(), c.getSession().getSessionIdLSB()))); + if (this.lwM2mClientsByEndpoint.size() > 0) { + lwM2mClient = this.lwM2mClientsByEndpoint.values().stream().filter(isClientFilter).findAny().orElse(null); + } + if (lwM2mClient == null && this.lwM2mClientsByRegistrationId.size() > 0) { + lwM2mClient = this.lwM2mClientsByRegistrationId.values().stream().filter(isClientFilter).findAny().orElse(null); + } if (lwM2mClient == null) { log.warn("Device TimeOut? lwM2mClient is null."); - log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}]", sessionInfo, lwM2mClientsByEndpoint.values().size()); + log.warn("SessionInfo input [{}], lwM2mClientsByEndpoint size: [{}] lwM2mClientsByRegistrationId: [{}]", sessionInfo, lwM2mClientsByEndpoint.values(), lwM2mClientsByRegistrationId.values()); log.error("", new RuntimeException()); } return lwM2mClient; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 6b289bac59..95d69e3f7e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -61,6 +61,7 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; @@ -194,11 +195,17 @@ public class LwM2mFwSwUpdate { } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { Registration registration = this.getLwM2MClient().getRegistration(); // String api = handler.config.getHostRequests(); - String api = "0.0.0.0"; + String api = "176.36.143.9"; int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); +// String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + "api/v1/test_url" + "/" + this.currentId.toString(); String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); +// home +// String uri = "coap://176.100.5.79:5683/api/v1/test_url/firmware"; +// office +// String uri = "coap://176.36.143.9:5683/api/v1/test_url/firmware"; log.warn("89) coapUri: [{}]", uri); - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, +// request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, + request.sendAllRequest(this.lwM2MClient, targetIdVer, READ, null, uri, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html index b164e168cf..e75aedd47c 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.html @@ -142,42 +142,98 @@
-
- - {{ 'device-profile.lwm2m.client-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.client-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.client-strategy' | translate: - {count: 2} }} - - -
-
- - {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 2} }} - {{ 'device-profile.lwm2m.fw-update-strategy' | translate: - {count: 3} }} - - - - {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} - - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 1} }} - {{ 'device-profile.lwm2m.sw-update-strategy' | translate: - {count: 2} }} - - -
+ +
+ + + +
{{ 'device-profile.lwm2m.client-strategy' | translate | uppercase }}
+
+
+ +
+ + {{ 'device-profile.lwm2m.client-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.client-strategy-connect' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.client-strategy-connect' | translate: + {count: 2} }} + + +
+
+
+
+ + + +
{{ 'device-profile.lwm2m.ota-update-strategy' | translate | uppercase }}
+
+
+ +
+ + {{ 'device-profile.lwm2m.fw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 2} }} + {{ 'device-profile.lwm2m.fw-update-strategy' | translate: + {count: 3} }} + + + + {{ 'device-profile.lwm2m.sw-update-strategy-label' | translate }} + + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: + {count: 1} }} + {{ 'device-profile.lwm2m.sw-update-strategy' | translate: + {count: 2} }} + + +
+
+
+ + + +
{{ 'device-profile.lwm2m.ota-update-recourse' | translate | uppercase }}
+
+
+ +
+
+ + {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.fw-update-recourse' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+ + {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} + + + {{ 'device-profile.lwm2m.sw-update-recourse' | translate }} + {{ 'device-profile.lwm2m.required' | translate }} + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 7d2856e1dc..3e83d1c6a6 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -22,6 +22,8 @@ import { ATTRIBUTE, BINDING_MODE, BINDING_MODE_NAMES, + DEFAULT_FW_UPDATE_RESOURCE, + DEFAULT_SW_UPDATE_RESOURCE, getDefaultProfileConfig, Instance, INSTANCES, @@ -41,6 +43,7 @@ import { Direction } from '@shared/models/page/sort-order'; import _ from 'lodash'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { MatSelectChange } from '@angular/material/select'; @Component({ selector: 'tb-profile-lwm2m-device-transport-configuration', @@ -67,6 +70,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro bootstrapServer: string; lwm2mServer: string; sortFunction: (key: string, value: object) => object; + isFwUpdateStrategy: boolean; + isSwUpdateStrategy: boolean; get required(): boolean { return this.requiredValue; @@ -95,6 +100,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro clientStrategy: [1, []], fwUpdateStrategy: [1, []], swUpdateStrategy: [1, []], + fwUpdateRecourse: ['', []], + swUpdateRecourse: ['', []] }); this.lwm2mDeviceConfigFormGroup = this.fb.group({ configurationJson: [null, Validators.required] @@ -169,6 +176,13 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { + debugger + let fwResource = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2' && + isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; + let swResource = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2' && + isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? + this.configurationValue.clientLwM2mSettings.swUpdateRecourse : ''; this.lwm2mDeviceProfileFormGroup.patchValue({ objectIds: value, observeAttrTelemetry: this.getObserveAttrTelemetryObjects(value.objectsList), @@ -181,9 +195,17 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro lwm2mServer: this.configurationValue.bootstrap.lwm2mServer, clientStrategy: this.configurationValue.clientLwM2mSettings.clientStrategy, fwUpdateStrategy: this.configurationValue.clientLwM2mSettings.fwUpdateStrategy, - swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy + swUpdateStrategy: this.configurationValue.clientLwM2mSettings.swUpdateStrategy, + fwUpdateRecourse: fwResource, + swUpdateRecourse: swResource }, {emitEvent: false}); + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = fwResource; + this.configurationValue.clientLwM2mSettings.swUpdateRecourse = swResource; + this.isFwUpdateStrategy = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2'; + this.isSwUpdateStrategy = this.configurationValue.clientLwM2mSettings.swUpdateStrategy === '2'; + this.otaUpdateSwStrategyValidate(); + this.otaUpdateFwStrategyValidate(); } private updateModel = (): void => { @@ -215,6 +237,8 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro this.configurationValue.clientLwM2mSettings.clientStrategy = config.clientStrategy; this.configurationValue.clientLwM2mSettings.fwUpdateStrategy = config.fwUpdateStrategy; this.configurationValue.clientLwM2mSettings.swUpdateStrategy = config.swUpdateStrategy; + this.configurationValue.clientLwM2mSettings.fwUpdateRecourse = config.fwUpdateRecourse; + this.configurationValue.clientLwM2mSettings.swUpdateRecourse = config.swUpdateRecourse; this.upDateJsonAllConfig(); this.updateModel(); } @@ -264,7 +288,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro const pathParameter = Array.from(path.split('/'), String); const objectLwM2M = clientObserveAttrTelemetry.find(x => x.keyId === pathParameter[0]); if (objectLwM2M) { - const instance = this.updateInInstanceKeyName (objectLwM2M.instances[0], +pathParameter[1]); + const instance = this.updateInInstanceKeyName(objectLwM2M.instances[0], +pathParameter[1]); objectLwM2M.instances.push(instance); } }); @@ -489,4 +513,56 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } }); } + + changeFwUpdateStrategy($event: MatSelectChange) { + debugger + if ($event.value === '2') { + this.isFwUpdateStrategy = true; + if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').value)) { + this.lwm2mDeviceProfileFormGroup.patchValue({ + fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE + }, + {emitEvent: false}); + } + } else { + this.isFwUpdateStrategy = false; + } + this.otaUpdateFwStrategyValidate(); + } + + changeSwUpdateStrategy($event: MatSelectChange) { + debugger + if ($event.value === '2') { + this.isSwUpdateStrategy = true; + if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').value)) { + this.lwm2mDeviceProfileFormGroup.patchValue({ + swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE + }, + {emitEvent: false}); + + } + } else { + this.isSwUpdateStrategy = false; + } + this.otaUpdateSwStrategyValidate(); + } + + private otaUpdateFwStrategyValidate(): void { + if (this.isFwUpdateStrategy) { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').setValidators([Validators.required]); + } else { + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').clearValidators(); + } + this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').updateValueAndValidity(); + } + + private otaUpdateSwStrategyValidate(): void { + if (this.isSwUpdateStrategy) { + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').setValidators([Validators.required]); + } else { + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').clearValidators(); + } + this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').updateValueAndValidity(); + } + } diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index 2a12f892e2..fb85eb0858 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -28,7 +28,7 @@ export const TELEMETRY = 'telemetry'; export const KEY_NAME = 'keyName'; export const DEFAULT_ID_SERVER = 123; export const DEFAULT_ID_BOOTSTRAP = 111; -export const DEFAULT_HOST_NAME = 'localhost'; +export const DEFAULT_LOCAL_HOST_NAME = 'localhost'; export const DEFAULT_PORT_SERVER_NO_SEC = 5685; export const DEFAULT_PORT_BOOTSTRAP_NO_SEC = 5687; export const DEFAULT_CLIENT_HOLD_OFF_TIME = 1; @@ -43,6 +43,11 @@ export const KEY_REGEXP_HEX_DEC = /^[-+]?[0-9A-Fa-f]+\.?[0-9A-Fa-f]*?$/; export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; +export const DEFAULT_OTA_UPDATE_PROTOCOL = "coap://"; +export const DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; +export const DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; +export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE; +export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE; export enum BINDING_MODE { @@ -168,6 +173,8 @@ export interface ClientLwM2mSettings { clientStrategy: string; fwUpdateStrategy: string; swUpdateStrategy: string; + fwUpdateRecourse: string; + swUpdateRecourse: string; } export interface ObservableAttributes { @@ -231,7 +238,7 @@ export function getDefaultProfileConfig(hostname?: any): Lwm2mProfileConfigModel return { clientLwM2mSettings: getDefaultProfileClientLwM2mSettingsConfig(), observeAttr: getDefaultProfileObserveAttrConfig(), - bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_HOST_NAME) + bootstrap: getDefaultProfileBootstrapSecurityConfig((hostname) ? hostname : DEFAULT_LOCAL_HOST_NAME) }; } @@ -239,7 +246,9 @@ function getDefaultProfileClientLwM2mSettingsConfig(): ClientLwM2mSettings { return { clientStrategy: "1", fwUpdateStrategy: "1", - swUpdateStrategy: "1" + swUpdateStrategy: "1", + fwUpdateRecourse: DEFAULT_FW_UPDATE_RESOURCE, + swUpdateRecourse: DEFAULT_SW_UPDATE_RESOURCE }; } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 5fbb6ed3fb..90a93c09e6 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1262,6 +1262,7 @@ "bootstrap-server-account-timeout": "Account after the timeout", "bootstrap-server-account-timeout-tip": "Bootstrap-Server Account after the timeout value given by this resource.", "others-tab": "Other settings...", + "client-strategy": "Client strategy when connecting", "client-strategy-label": "Strategy", "client-strategy-connect": "{ count, plural, 1 {1: Only Observe Request to the client after the initial connection} other {2: Read All Resources & Observe Request to the client after registration} }", "client-strategy-tip": "{ count, plural, 1 {Strategy 1: After the initial connection of the LWM2M Client, the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} other {Strategy 2: After the registration, request the client to read all the resource values for all objects that the LWM2M client has,\n then execute: the server sends Observe resources Request to the client, those resources that are marked as observation in the Device profile and which exist on the LWM2M client.} }", @@ -1270,9 +1271,7 @@ "fw-update-strategy": "{ count, plural, 1 {Push firmware update as binary file using Object 5 and Resource 0 (Package).} 2 {Auto-generate unique CoAP URL to download the package and push firmware update as Object 5 and Resource 1 (Package URI).} other {Push firmware update as binary file using Object 19 and Resource 0 (Data).} }", "sw-update-strategy-label": "Software update strategy", "sw-update-strategy": "{ count, plural, 1 {Push binary file using Object 9 and Resource 2 (Package).} other {Auto-generate unique CoAP URL to download the package and push software update using Object 9 and Resource 3 (Package URI).} }", - "blockwise-settings": "Blockwise settings", - "blockwise-enabled": "Enable blockwise", - "blockwise-status-lifetime": "Blockwise Status Lifetime", + "ota-update-recourse": "Ota update Coap recourse", "fw-update-recourse": "Firmware update Coap recourse", "sw-update-recourse": "Software update Coap recourse", "config-json-tab": "Json Config Profile Device" From 26bc7841aedc4335f9169e43c3bc7ed796b5b65b Mon Sep 17 00:00:00 2001 From: nickAS21 Date: Thu, 10 Jun 2021 13:08:13 +0300 Subject: [PATCH 75/75] LWM2M: front del debugger --- .../transport/coap/CoapTransportResource.java | 2 +- .../lwm2m/server/client/LwM2mFwSwUpdate.java | 16 +++------------- ...-profile-transport-configuration.component.ts | 3 --- .../device/lwm2m/lwm2m-profile-config.models.ts | 6 ++---- 4 files changed, 6 insertions(+), 21 deletions(-) diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index a1a2a4b656..33f122fa3f 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -398,7 +398,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { private Optional decodeCredentials(Request request) { List uriPath = request.getOptions().getUriPath(); - if (uriPath.size() >= ACCESS_TOKEN_POSITION) { + if (uriPath.size() > ACCESS_TOKEN_POSITION) { return Optional.of(new DeviceTokenCredentials(uriPath.get(ACCESS_TOKEN_POSITION - 1))); } else { return Optional.empty(); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index 95d69e3f7e..6828761fb0 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -61,7 +61,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; @@ -193,19 +192,10 @@ public class LwM2mFwSwUpdate { request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE, firmwareChunk, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { - Registration registration = this.getLwM2MClient().getRegistration(); -// String api = handler.config.getHostRequests(); - String api = "176.36.143.9"; - int port = registration.getIdentity().isSecure() ? handler.config.getSecurePort() : handler.config.getPort(); -// String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + "api/v1/test_url" + "/" + this.currentId.toString(); - String uri = "coap://" + api + ":" + Integer.valueOf(port) + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); -// home -// String uri = "coap://176.100.5.79:5683/api/v1/test_url/firmware"; -// office -// String uri = "coap://176.36.143.9:5683/api/v1/test_url/firmware"; + String apiFont = "coap://176.36.143.9:5685"; + String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); -// request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, - request.sendAllRequest(this.lwM2MClient, targetIdVer, READ, null, + request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, uri, handler.config.getTimeout(), this.rpcRequest); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts index 3e83d1c6a6..bc9b36271d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts @@ -176,7 +176,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } private updateWriteValue = (value: ModelValue): void => { - debugger let fwResource = this.configurationValue.clientLwM2mSettings.fwUpdateStrategy === '2' && isDefinedAndNotNull(this.configurationValue.clientLwM2mSettings.fwUpdateRecourse) ? this.configurationValue.clientLwM2mSettings.fwUpdateRecourse : ''; @@ -515,7 +514,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } changeFwUpdateStrategy($event: MatSelectChange) { - debugger if ($event.value === '2') { this.isFwUpdateStrategy = true; if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('fwUpdateRecourse').value)) { @@ -531,7 +529,6 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro } changeSwUpdateStrategy($event: MatSelectChange) { - debugger if ($event.value === '2') { this.isSwUpdateStrategy = true; if (isEmpty(this.lwm2mDeviceProfileFormGroup.get('swUpdateRecourse').value)) { diff --git a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts index fb85eb0858..ff8225a27d 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-profile-config.models.ts @@ -44,10 +44,8 @@ export const KEY_REGEXP_NUMBER = /^(\-?|\+?)\d*$/; export const INSTANCES_ID_VALUE_MIN = 0; export const INSTANCES_ID_VALUE_MAX = 65535; export const DEFAULT_OTA_UPDATE_PROTOCOL = "coap://"; -export const DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; -export const DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE = "softwareUpdateCoapRecourse"; -export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_FIRMWARE_UPDATE_COAP_RECOURSE; -export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC + "/" + DEFAULT_SOFTWARE_UPDATE_COAP_RECOURSE; +export const DEFAULT_FW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; +export const DEFAULT_SW_UPDATE_RESOURCE = DEFAULT_OTA_UPDATE_PROTOCOL + DEFAULT_LOCAL_HOST_NAME + ":"+ DEFAULT_PORT_SERVER_NO_SEC; export enum BINDING_MODE {