Commit fa3bbb2851a443d088ceb86e08f1daa41111e54a

Authored by Vladyslav_Prykhodko
1 parent b978f0f7

UI: And confirm dialog after change ota package in device profile

@@ -14,16 +14,21 @@ @@ -14,16 +14,21 @@
14 /// limitations under the License. 14 /// limitations under the License.
15 /// 15 ///
16 16
17 -import {Injectable} from '@angular/core';  
18 -import {HttpClient} from '@angular/common/http';  
19 -import {PageLink} from '@shared/models/page/page-link';  
20 -import {defaultHttpOptionsFromConfig, RequestConfig} from './http-utils';  
21 -import {Observable} from 'rxjs';  
22 -import {PageData} from '@shared/models/page/page-data';  
23 -import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models';  
24 -import {isDefinedAndNotNull, isEmptyStr} from '@core/utils';  
25 -import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';  
26 -import {SortOrder} from '@shared/models/page/sort-order'; 17 +import { Injectable } from '@angular/core';
  18 +import { HttpClient } from '@angular/common/http';
  19 +import { PageLink } from '@shared/models/page/page-link';
  20 +import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
  21 +import { forkJoin, Observable, of, throwError } from 'rxjs';
  22 +import { PageData } from '@shared/models/page/page-data';
  23 +import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models';
  24 +import { isDefinedAndNotNull, isEmptyStr } from '@core/utils';
  25 +import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models';
  26 +import { SortOrder } from '@shared/models/page/sort-order';
  27 +import { OtaPackageService } from '@core/http/ota-package.service';
  28 +import { OtaUpdateType } from '@shared/models/ota-package.models';
  29 +import { mergeMap } from 'rxjs/operators';
  30 +import { DialogService } from '@core/services/dialog.service';
  31 +import { TranslateService } from '@ngx-translate/core';
27 32
28 @Injectable({ 33 @Injectable({
29 providedIn: 'root' 34 providedIn: 'root'
@@ -31,7 +36,10 @@ import {SortOrder} from '@shared/models/page/sort-order'; @@ -31,7 +36,10 @@ import {SortOrder} from '@shared/models/page/sort-order';
31 export class DeviceProfileService { 36 export class DeviceProfileService {
32 37
33 constructor( 38 constructor(
34 - private http: HttpClient 39 + private http: HttpClient,
  40 + private otaPackageService: OtaPackageService,
  41 + private dialogService: DialogService,
  42 + private translate: TranslateService
35 ) { 43 ) {
36 } 44 }
37 45
@@ -70,6 +78,34 @@ export class DeviceProfileService { @@ -70,6 +78,34 @@ export class DeviceProfileService {
70 ); 78 );
71 } 79 }
72 80
  81 + public saveDeviceProfileAndConfirmOtaChange(originDeviceProfile: DeviceProfile, deviceProfile: DeviceProfile,
  82 + config?: RequestConfig): Observable<DeviceProfile> {
  83 + const tasks: Observable<number>[] = [];
  84 + if (originDeviceProfile?.id?.id && originDeviceProfile.firmwareId?.id !== deviceProfile.firmwareId?.id) {
  85 + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.FIRMWARE, deviceProfile.id.id));
  86 + } else {
  87 + tasks.push(of(0));
  88 + }
  89 + if (originDeviceProfile?.id?.id && originDeviceProfile.softwareId?.id !== deviceProfile.softwareId?.id) {
  90 + tasks.push(this.otaPackageService.countUpdateDeviceAfterChangePackage(OtaUpdateType.SOFTWARE, deviceProfile.id.id));
  91 + } else {
  92 + tasks.push(of(0));
  93 + }
  94 + return forkJoin(tasks).pipe(
  95 + mergeMap(([deviceFirmwareUpdate, deviceSoftwareUpdate]) => {
  96 + let text = '';
  97 + if (deviceFirmwareUpdate > 0) {
  98 + text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate});
  99 + }
  100 + if (deviceSoftwareUpdate > 0) {
  101 + text += text.length ? '<br/>' : '';
  102 + text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate});
  103 + }
  104 + return text !== '' ? this.dialogService.confirm('', text) : of(true);
  105 + }),
  106 + mergeMap((update) => update ? this.saveDeviceProfile(deviceProfile, config) : throwError('Canceled saving device profiles')));
  107 + }
  108 +
73 public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable<DeviceProfile> { 109 public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable<DeviceProfile> {
74 return this.http.post<DeviceProfile>('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config)); 110 return this.http.post<DeviceProfile>('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config));
75 } 111 }
@@ -120,4 +120,8 @@ export class OtaPackageService { @@ -120,4 +120,8 @@ export class OtaPackageService {
120 return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config)); 120 return this.http.delete(`/api/otaPackage/${otaPackageId}`, defaultHttpOptionsFromConfig(config));
121 } 121 }
122 122
  123 + public countUpdateDeviceAfterChangePackage(type: OtaUpdateType, deviceProfileId: string, config?: RequestConfig): Observable<number> {
  124 + return this.http.get<number>(`/api/devices/count/${type}?deviceProfileId=${deviceProfileId}`, defaultHttpOptionsFromConfig(config));
  125 + }
  126 +
123 } 127 }
@@ -280,7 +280,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV @@ -280,7 +280,7 @@ export class EntityDetailsPanelComponent extends PageComponent implements AfterV
280 editingEntity.additionalInfo = 280 editingEntity.additionalInfo =
281 mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo); 281 mergeDeep((this.editingEntity as any).additionalInfo, this.entityComponent.entityFormValue()?.additionalInfo);
282 } 282 }
283 - this.entitiesTableConfig.saveEntity(editingEntity).subscribe( 283 + this.entitiesTableConfig.saveEntity(editingEntity, this.editingEntity).subscribe(
284 (entity) => { 284 (entity) => {
285 this.entity = entity; 285 this.entity = entity;
286 this.entityComponent.entity = entity; 286 this.entityComponent.entity = entity;
@@ -66,5 +66,5 @@ @@ -66,5 +66,5 @@
66 <mat-error *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').hasError('required')"> 66 <mat-error *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').hasError('required')">
67 {{ 'device-profile.device-profile-required' | translate }} 67 {{ 'device-profile.device-profile-required' | translate }}
68 </mat-error> 68 </mat-error>
69 - <mat-hint *ngIf="!!hint">{{ hint | translate }}</mat-hint> 69 + <mat-hint *ngIf="hint && !disabled">{{ hint | translate }}</mat-hint>
70 </mat-form-field> 70 </mat-form-field>
@@ -90,7 +90,7 @@ export class DeviceProfileDialogComponent extends @@ -90,7 +90,7 @@ export class DeviceProfileDialogComponent extends
90 this.submitted = true; 90 this.submitted = true;
91 if (this.deviceProfileComponent.entityForm.valid) { 91 if (this.deviceProfileComponent.entityForm.valid) {
92 this.deviceProfile = {...this.deviceProfile, ...this.deviceProfileComponent.entityFormValue()}; 92 this.deviceProfile = {...this.deviceProfile, ...this.deviceProfileComponent.entityFormValue()};
93 - this.deviceProfileService.saveDeviceProfile(this.deviceProfile).subscribe( 93 + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(this.deviceProfile, this.deviceProfile).subscribe(
94 (deviceProfile) => { 94 (deviceProfile) => {
95 this.dialogRef.close(deviceProfile); 95 this.dialogRef.close(deviceProfile);
96 } 96 }
@@ -37,7 +37,7 @@ export type EntityStringFunction<T extends BaseData<HasId>> = (entity: T) => str @@ -37,7 +37,7 @@ export type EntityStringFunction<T extends BaseData<HasId>> = (entity: T) => str
37 export type EntityVoidFunction<T extends BaseData<HasId>> = (entity: T) => void; 37 export type EntityVoidFunction<T extends BaseData<HasId>> = (entity: T) => void;
38 export type EntityIdsVoidFunction<T extends BaseData<HasId>> = (ids: HasUUID[]) => void; 38 export type EntityIdsVoidFunction<T extends BaseData<HasId>> = (ids: HasUUID[]) => void;
39 export type EntityCountStringFunction = (count: number) => string; 39 export type EntityCountStringFunction = (count: number) => string;
40 -export type EntityTwoWayOperation<T extends BaseData<HasId>> = (entity: T) => Observable<T>; 40 +export type EntityTwoWayOperation<T extends BaseData<HasId>> = (entity: T, originalEntity?: T) => Observable<T>;
41 export type EntityByIdOperation<T extends BaseData<HasId>> = (id: HasUUID) => Observable<T>; 41 export type EntityByIdOperation<T extends BaseData<HasId>> = (id: HasUUID) => Observable<T>;
42 export type EntityIdOneWayOperation = (id: HasUUID) => Observable<any>; 42 export type EntityIdOneWayOperation = (id: HasUUID) => Observable<any>;
43 export type EntityActionFunction<T extends BaseData<HasId>> = (action: EntityAction<T>) => boolean; 43 export type EntityActionFunction<T extends BaseData<HasId>> = (action: EntityAction<T>) => boolean;
@@ -173,7 +173,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P @@ -173,7 +173,7 @@ export class EntityTableConfig<T extends BaseData<HasId>, P extends PageLink = P
173 deleteEntitiesTitle: EntityCountStringFunction = () => ''; 173 deleteEntitiesTitle: EntityCountStringFunction = () => '';
174 deleteEntitiesContent: EntityCountStringFunction = () => ''; 174 deleteEntitiesContent: EntityCountStringFunction = () => '';
175 loadEntity: EntityByIdOperation<T | L> = () => of(); 175 loadEntity: EntityByIdOperation<T | L> = () => of();
176 - saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity); 176 + saveEntity: EntityTwoWayOperation<T> = (entity, originalEntity) => of(entity);
177 deleteEntity: EntityIdOneWayOperation = () => of(); 177 deleteEntity: EntityIdOneWayOperation = () => of();
178 entitiesFetchFunction: EntitiesFetchFunction<L, P> = () => of(emptyPageData<L>()); 178 entitiesFetchFunction: EntitiesFetchFunction<L, P> = () => of(emptyPageData<L>());
179 onEntityAction: EntityActionFunction<T> = () => false; 179 onEntityAction: EntityActionFunction<T> = () => false;
@@ -104,7 +104,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -104,7 +104,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
104 104
105 this.config.entitiesFetchFunction = pageLink => this.deviceProfileService.getDeviceProfiles(pageLink); 105 this.config.entitiesFetchFunction = pageLink => this.deviceProfileService.getDeviceProfiles(pageLink);
106 this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id); 106 this.config.loadEntity = id => this.deviceProfileService.getDeviceProfile(id.id);
107 - this.config.saveEntity = deviceProfile => this.deviceProfileService.saveDeviceProfile(deviceProfile); 107 + this.config.saveEntity = (deviceProfile, originDeviceProfile) =>
  108 + this.deviceProfileService.saveDeviceProfileAndConfirmOtaChange(originDeviceProfile, deviceProfile);
108 this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id); 109 this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id);
109 this.config.onEntityAction = action => this.onDeviceProfileAction(action); 110 this.config.onEntityAction = action => this.onDeviceProfileAction(action);
110 this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default; 111 this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default;
@@ -69,6 +69,7 @@ @@ -69,6 +69,7 @@
69 <tb-device-profile-autocomplete 69 <tb-device-profile-autocomplete
70 formControlName="deviceProfileId" 70 formControlName="deviceProfileId"
71 required 71 required
  72 + [ngStyle]="{'padding-bottom': isAdd ? '16px': 0}"
72 [hint]="'ota-update.chose-compatible-device-profile'" 73 [hint]="'ota-update.chose-compatible-device-profile'"
73 [editProfileEnabled]="false" 74 [editProfileEnabled]="false"
74 [addNewProfile]="false" 75 [addNewProfile]="false"
@@ -2155,7 +2155,9 @@ @@ -2155,7 +2155,9 @@
2155 "checksum": "Checksum", 2155 "checksum": "Checksum",
2156 "checksum-algorithm": "Checksum algorithm", 2156 "checksum-algorithm": "Checksum algorithm",
2157 "checksum-copied-message": "Package checksum has been copied to clipboard", 2157 "checksum-copied-message": "Package checksum has been copied to clipboard",
2158 - "chose-compatible-device-profile": "Choose compatible device profile", 2158 + "change-firmware": "You have changed the firmware. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.",
  2159 + "change-software": "You have changed the software. This may cause update of the { count, plural, 1 {1 device} other {# devices} }.",
  2160 + "chose-compatible-device-profile": "Choose compatible device profile. The uploaded package will be available only for devices with the chosen profile.",
2159 "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices", 2161 "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices",
2160 "chose-software-distributed-device": "Choose software that will be distributed to the devices", 2162 "chose-software-distributed-device": "Choose software that will be distributed to the devices",
2161 "content-type": "Content type", 2163 "content-type": "Content type",