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 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 33 @Injectable({
29 34 providedIn: 'root'
... ... @@ -31,7 +36,10 @@ import {SortOrder} from '@shared/models/page/sort-order';
31 36 export class DeviceProfileService {
32 37
33 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 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 109 public saveDeviceProfile(deviceProfile: DeviceProfile, config?: RequestConfig): Observable<DeviceProfile> {
74 110 return this.http.post<DeviceProfile>('/api/deviceProfile', deviceProfile, defaultHttpOptionsFromConfig(config));
75 111 }
... ...
... ... @@ -120,4 +120,8 @@ export class OtaPackageService {
120 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 280 editingEntity.additionalInfo =
281 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 284 (entity) => {
285 285 this.entity = entity;
286 286 this.entityComponent.entity = entity;
... ...
... ... @@ -66,5 +66,5 @@
66 66 <mat-error *ngIf="selectDeviceProfileFormGroup.get('deviceProfile').hasError('required')">
67 67 {{ 'device-profile.device-profile-required' | translate }}
68 68 </mat-error>
69   - <mat-hint *ngIf="!!hint">{{ hint | translate }}</mat-hint>
  69 + <mat-hint *ngIf="hint && !disabled">{{ hint | translate }}</mat-hint>
70 70 </mat-form-field>
... ...
... ... @@ -90,7 +90,7 @@ export class DeviceProfileDialogComponent extends
90 90 this.submitted = true;
91 91 if (this.deviceProfileComponent.entityForm.valid) {
92 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 94 (deviceProfile) => {
95 95 this.dialogRef.close(deviceProfile);
96 96 }
... ...
... ... @@ -37,7 +37,7 @@ export type EntityStringFunction<T extends BaseData<HasId>> = (entity: T) => str
37 37 export type EntityVoidFunction<T extends BaseData<HasId>> = (entity: T) => void;
38 38 export type EntityIdsVoidFunction<T extends BaseData<HasId>> = (ids: HasUUID[]) => void;
39 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 41 export type EntityByIdOperation<T extends BaseData<HasId>> = (id: HasUUID) => Observable<T>;
42 42 export type EntityIdOneWayOperation = (id: HasUUID) => Observable<any>;
43 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 173 deleteEntitiesTitle: EntityCountStringFunction = () => '';
174 174 deleteEntitiesContent: EntityCountStringFunction = () => '';
175 175 loadEntity: EntityByIdOperation<T | L> = () => of();
176   - saveEntity: EntityTwoWayOperation<T> = (entity) => of(entity);
  176 + saveEntity: EntityTwoWayOperation<T> = (entity, originalEntity) => of(entity);
177 177 deleteEntity: EntityIdOneWayOperation = () => of();
178 178 entitiesFetchFunction: EntitiesFetchFunction<L, P> = () => of(emptyPageData<L>());
179 179 onEntityAction: EntityActionFunction<T> = () => false;
... ...
... ... @@ -104,7 +104,8 @@ export class DeviceProfilesTableConfigResolver implements Resolve<EntityTableCon
104 104
105 105 this.config.entitiesFetchFunction = pageLink => this.deviceProfileService.getDeviceProfiles(pageLink);
106 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 109 this.config.deleteEntity = id => this.deviceProfileService.deleteDeviceProfile(id.id);
109 110 this.config.onEntityAction = action => this.onDeviceProfileAction(action);
110 111 this.config.deleteEnabled = (deviceProfile) => deviceProfile && !deviceProfile.default;
... ...
... ... @@ -69,6 +69,7 @@
69 69 <tb-device-profile-autocomplete
70 70 formControlName="deviceProfileId"
71 71 required
  72 + [ngStyle]="{'padding-bottom': isAdd ? '16px': 0}"
72 73 [hint]="'ota-update.chose-compatible-device-profile'"
73 74 [editProfileEnabled]="false"
74 75 [addNewProfile]="false"
... ...
... ... @@ -2155,7 +2155,9 @@
2155 2155 "checksum": "Checksum",
2156 2156 "checksum-algorithm": "Checksum algorithm",
2157 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 2161 "chose-firmware-distributed-device": "Choose firmware that will be distributed to the devices",
2160 2162 "chose-software-distributed-device": "Choose software that will be distributed to the devices",
2161 2163 "content-type": "Content type",
... ...