Browse Source

UI: OTA updates add support external URL

pull/4695/head
Vladyslav_Prykhodko 5 years ago
parent
commit
ce1a3ea567
  1. 4
      ui-ngx/src/app/core/http/device-profile.service.ts
  2. 42
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts
  3. 96
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html
  4. 42
      ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.ts
  5. 1
      ui-ngx/src/app/shared/models/ota-package.models.ts
  6. 13
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
ui-ngx/src/app/core/http/device-profile.service.ts

@ -98,10 +98,10 @@ export class DeviceProfileService {
text += this.translate.instant('ota-update.change-firmware', {count: deviceFirmwareUpdate});
}
if (deviceSoftwareUpdate > 0) {
text += text.length ? '<br/>' : '';
text += text.length ? ' ' : '';
text += this.translate.instant('ota-update.change-software', {count: deviceSoftwareUpdate});
}
return text !== '' ? this.dialogService.confirm('', text) : of(true);
return text !== '' ? this.dialogService.confirm('', text, null, this.translate.instant('common.proceed')) : of(true);
}),
mergeMap((update) => update ? this.saveDeviceProfile(deviceProfile, config) : throwError('Canceled saving device profiles')));
}

42
ui-ngx/src/app/modules/home/pages/ota-update/ota-update-table-config.resolve.ts

@ -35,6 +35,10 @@ 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';
@Injectable()
export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<OtaPackage, PageLink, OtaPackageInfo>> {
@ -44,8 +48,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
constructor(private translate: TranslateService,
private datePipe: DatePipe,
private store: Store<AppState>,
private otaPackageService: OtaPackageService,
private fileSize: FileSizePipe) {
private fileSize: FileSizePipe,
private clipboardService: ClipboardService) {
this.config.entityType = EntityType.OTA_PACKAGE;
this.config.entityComponent = OtaUpdateComponent;
this.config.entityTranslations = entityTypeTranslations.get(EntityType.OTA_PACKAGE);
@ -62,14 +68,20 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
}),
new EntityTableColumn<OtaPackageInfo>('fileName', 'ota-update.file-name', '25%'),
new EntityTableColumn<OtaPackageInfo>('dataSize', 'ota-update.file-size', '70px', entity => {
return this.fileSize.transform(entity.dataSize || 0);
return entity.dataSize ? this.fileSize.transform(entity.dataSize) : '';
}),
new EntityTableColumn<OtaPackageInfo>('checksum', 'ota-update.checksum', '540px', entity => {
return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`;
return entity.checksum ? `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}` : '';
}, () => ({}), false)
);
this.config.cellActionDescriptors.push(
{
name: this.translate.instant('ota-update.copy-checksum'),
icon: 'content_copy',
isEnabled: (otaPackage) => !!otaPackage.checksum,
onAction: ($event, entity) => this.copyPackageChecksum($event, entity)
},
{
name: this.translate.instant('ota-update.download'),
icon: 'file_download',
@ -101,7 +113,26 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
if ($event) {
$event.stopPropagation();
}
this.otaPackageService.downloadOtaPackage(otaPackageInfo.id.id).subscribe();
if (otaPackageInfo.url) {
window.open(otaPackageInfo.url, '_blank');
} else {
this.otaPackageService.downloadOtaPackage(otaPackageInfo.id.id).subscribe();
}
}
copyPackageChecksum($event: Event, otaPackageInfo: OtaPackageInfo) {
if ($event) {
$event.stopPropagation();
}
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'
}));
}
onPackageAction(action: EntityAction<OtaPackageInfo>): boolean {
@ -109,6 +140,9 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
case 'uploadPackage':
this.exportPackage(action.event, action.entity);
return true;
case 'copyChecksum':
this.copyPackageChecksum(action.event, action.entity);
return true;
}
return false;
}

96
ui-ngx/src/app/modules/home/pages/ota-update/ota-update.component.html

@ -31,6 +31,7 @@
<div fxLayout="row" fxLayout.xs="column">
<button mat-raised-button
ngxClipboard
[disabled]="(isLoading$ | async)"
(cbOnSuccess)="onPackageIdCopied()"
[cbContent]="entity?.id?.id"
[fxShow]="!isEdit">
@ -38,10 +39,9 @@
<span translate>ota-update.copyId</span>
</button>
<button mat-raised-button
ngxClipboard
(cbOnSuccess)="onPackageChecksumCopied()"
[cbContent]="entity?.checksum"
[fxShow]="!isEdit">
[disabled]="(isLoading$ | async)"
(click)="onEntityAction($event, 'copyChecksum')"
[fxShow]="!isEdit && entity?.checksum">
<mat-icon svgIcon="mdi:clipboard-arrow-left"></mat-icon>
<span translate>ota-update.copy-checksum</span>
</button>
@ -49,7 +49,7 @@
</div>
<div class="mat-padding" fxLayout="column" fxLayoutGap="8px">
<form [formGroup]="entityForm">
<fieldset [disabled]="(isLoading$ | async) || !isEdit" fxLayout="column" fxLayoutGap="8px">
<fieldset [disabled]="(isLoading$ | async) || !isEdit" fxLayout="column">
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field class="mat-block" fxFlex="45">
<mat-label translate>ota-update.title</mat-label>
@ -83,44 +83,68 @@
</mat-option>
</mat-select>
</mat-form-field>
<div class="mat-caption" translate *ngIf="isAdd">ota-update.warning-after-save-no-edit</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px" fxLayout.xs="column" fxLayout.md="column">
<mat-form-field class="mat-block" fxFlex="33">
<mat-label translate>ota-update.checksum-algorithm</mat-label>
<mat-select formControlName="checksumAlgorithm">
<mat-option *ngFor="let checksumAlgorithm of checksumAlgorithms" [value]="checksumAlgorithm">
{{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.checksum</mat-label>
<input matInput formControlName="checksum" type="text" [readonly]="!isAdd">
</mat-form-field>
</div>
<section *ngIf="isAdd">
<tb-file-input
formControlName="file"
workFromFileObj="true"
required
dropLabel="{{'ota-update.drop-file' | translate}}">
</tb-file-input>
<div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div>
<mat-radio-group formControlName="resource" fxLayoutGap="16px">
<mat-radio-button value="file">Upload binary file</mat-radio-button>
<mat-radio-button value="url">Use external URL</mat-radio-button>
</mat-radio-group>
</section>
<section *ngIf="!isAdd">
<div fxLayout="row" fxLayoutGap.gt-md="8px" fxLayoutGap.sm="8px" fxLayout.xs="column" fxLayout.md="column">
<section *ngIf="entityForm.get('resource').value === 'file'">
<section *ngIf="isAdd">
<tb-file-input
formControlName="file"
workFromFileObj="true"
[required]="entityForm.get('resource').value === 'file'"
dropLabel="{{'ota-update.drop-file' | translate}}">
</tb-file-input>
<mat-checkbox formControlName="generateChecksum" style="margin-top: 16px">
{{ 'ota-update.auto-generate-checksum' | translate }}
</mat-checkbox>
</section>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px"
fxLayout.xs="column" fxLayout.md="column" *ngIf="!(isAdd && this.entityForm.get('generateChecksum').value)">
<mat-form-field class="mat-block" fxFlex="33">
<mat-label translate>ota-update.file-name</mat-label>
<input matInput formControlName="fileName" type="text" readonly>
<mat-label translate>ota-update.checksum-algorithm</mat-label>
<mat-select formControlName="checksumAlgorithm">
<mat-option *ngFor="let checksumAlgorithm of checksumAlgorithms" [value]="checksumAlgorithm">
{{ checksumAlgorithmTranslationMap.get(checksumAlgorithm) }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.file-size-bytes</mat-label>
<input matInput formControlName="dataSize" type="text" readonly>
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.content-type</mat-label>
<input matInput formControlName="contentType" type="text" readonly>
<mat-label translate>ota-update.checksum</mat-label>
<input matInput formControlName="checksum" type="text">
<mat-hint *ngIf="isAdd" translate>ota-update.checksum-hint</mat-hint>
</mat-form-field>
</div>
<section *ngIf="!isAdd">
<div fxLayout="row" fxLayoutGap.gt-md="8px" fxLayoutGap.sm="8px" fxLayout.xs="column" fxLayout.md="column">
<mat-form-field class="mat-block" fxFlex="33">
<mat-label translate>ota-update.file-name</mat-label>
<input matInput formControlName="fileName" type="text">
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.file-size-bytes</mat-label>
<input matInput formControlName="dataSize" type="text">
</mat-form-field>
<mat-form-field class="mat-block" fxFlex>
<mat-label translate>ota-update.content-type</mat-label>
<input matInput formControlName="contentType" type="text">
</mat-form-field>
</div>
</section>
</section>
<section *ngIf="entityForm.get('resource').value === 'url'" style="margin-top: 8px">
<mat-form-field class="mat-block">
<mat-label translate>ota-update.url</mat-label>
<input matInput formControlName="url"
type="text"
[required]="entityForm.get('resource').value === 'url'">
<mat-error *ngIf="entityForm.get('url').hasError('required')" translate>
ota-update.url-required
</mat-error>
</mat-form-field>
</section>
<div formGroupName="additionalInfo">
<mat-form-field class="mat-block">

42
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<OtaPackage> 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<OtaPackage> 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<OtaPackage> 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<OtaPackage> 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<OtaPackage> 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<OtaPackage> 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);
}
}

1
ui-ngx/src/app/shared/models/ota-package.models.ts

@ -87,6 +87,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> {
title?: string;
version?: string;
hasData?: boolean;
url?: string;
fileName: string;
checksum?: string;
checksumAlgorithm?: ChecksumAlgorithm;

13
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."

Loading…
Cancel
Save