Commit fa3bbb2851a443d088ceb86e08f1daa41111e54a
1 parent
b978f0f7
UI: And confirm dialog after change ota package in device profile
Showing
9 changed files
with
62 additions
and
18 deletions
@@ -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", |