Commit 52d07fe080c99765b8844d87c292df013f1052ba

Authored by YevhenBondarenko
2 parents c9fbe743 5a880346

Merge branch 'feature/ota-tag' of github.com:YevhenBondarenko/thingsboard into feature/ota-tag

@@ -62,6 +62,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage @@ -62,6 +62,7 @@ public class OtaPackageInfo extends SearchTextBasedWithAdditionalInfo<OtaPackage
62 this.type = otaPackageInfo.getType(); 62 this.type = otaPackageInfo.getType();
63 this.title = otaPackageInfo.getTitle(); 63 this.title = otaPackageInfo.getTitle();
64 this.version = otaPackageInfo.getVersion(); 64 this.version = otaPackageInfo.getVersion();
  65 + this.tag = otaPackageInfo.getTag();
65 this.url = otaPackageInfo.getUrl(); 66 this.url = otaPackageInfo.getUrl();
66 this.hasData = otaPackageInfo.isHasData(); 67 this.hasData = otaPackageInfo.isHasData();
67 this.fileName = otaPackageInfo.getFileName(); 68 this.fileName = otaPackageInfo.getFileName();
@@ -145,6 +145,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen @@ -145,6 +145,7 @@ public class OtaPackageInfoEntity extends BaseSqlEntity<OtaPackageInfo> implemen
145 this.type = type; 145 this.type = type;
146 this.title = title; 146 this.title = title;
147 this.version = version; 147 this.version = version;
  148 + this.tag = tag;
148 this.url = url; 149 this.url = url;
149 this.fileName = fileName; 150 this.fileName = fileName;
150 this.contentType = contentType; 151 this.contentType = contentType;
@@ -59,9 +59,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot @@ -59,9 +59,10 @@ export class OtaUpdateTableConfigResolve implements Resolve<EntityTableConfig<Ot
59 59
60 this.config.columns.push( 60 this.config.columns.push(
61 new DateEntityTableColumn<OtaPackageInfo>('createdTime', 'common.created-time', this.datePipe, '150px'), 61 new DateEntityTableColumn<OtaPackageInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
62 - new EntityTableColumn<OtaPackageInfo>('title', 'ota-update.title', '20%'),  
63 - new EntityTableColumn<OtaPackageInfo>('version', 'ota-update.version', '20%'),  
64 - new EntityTableColumn<OtaPackageInfo>('type', 'ota-update.package-type', '20%', entity => { 62 + new EntityTableColumn<OtaPackageInfo>('title', 'ota-update.title', '15%'),
  63 + new EntityTableColumn<OtaPackageInfo>('version', 'ota-update.version', '15%'),
  64 + new EntityTableColumn<OtaPackageInfo>('tag', 'ota-update.version-tag', '15%'),
  65 + new EntityTableColumn<OtaPackageInfo>('type', 'ota-update.package-type', '15%', entity => {
65 return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type)); 66 return this.translate.instant(OtaUpdateTypeTranslationMap.get(entity.type));
66 }), 67 }),
67 new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => { 68 new EntityTableColumn<OtaPackageInfo>('url', 'ota-update.direct-url', '20%', entity => {
@@ -74,6 +74,11 @@ @@ -74,6 +74,11 @@
74 </mat-error> 74 </mat-error>
75 </mat-form-field> 75 </mat-form-field>
76 </div> 76 </div>
  77 + <mat-form-field class="mat-block" fxFlex style="margin-bottom: 8px">
  78 + <mat-label translate>ota-update.version-tag</mat-label>
  79 + <input matInput formControlName="tag" type="text" [readonly]="!isAdd">
  80 + <mat-hint *ngIf="isAdd" translate>ota-update.version-tag-hint</mat-hint>
  81 + </mat-form-field>
77 <tb-device-profile-autocomplete 82 <tb-device-profile-autocomplete
78 formControlName="deviceProfileId" 83 formControlName="deviceProfileId"
79 required 84 required
@@ -94,8 +99,8 @@ @@ -94,8 +99,8 @@
94 <section *ngIf="isAdd"> 99 <section *ngIf="isAdd">
95 <div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div> 100 <div class="mat-caption" style="margin: -8px 0 8px;" translate>ota-update.warning-after-save-no-edit</div>
96 <mat-radio-group formControlName="isURL" fxLayoutGap="16px"> 101 <mat-radio-group formControlName="isURL" fxLayoutGap="16px">
97 - <mat-radio-button [value]="false">Upload binary file</mat-radio-button>  
98 - <mat-radio-button [value]="true">Use external URL</mat-radio-button> 102 + <mat-radio-button [value]="false">{{ "ota-update.upload-binary-file" | translate }}</mat-radio-button>
  103 + <mat-radio-button [value]="true">{{ "ota-update.use-external-url" | translate }}</mat-radio-button>
99 </mat-radio-group> 104 </mat-radio-group>
100 </section> 105 </section>
101 <section *ngIf="!entityForm.get('isURL').value"> 106 <section *ngIf="!entityForm.get('isURL').value">
@@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
15 /// 15 ///
16 16
17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; 17 import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
18 -import { Subject } from 'rxjs'; 18 +import { combineLatest, Subject } from 'rxjs';
19 import { Store } from '@ngrx/store'; 19 import { Store } from '@ngrx/store';
20 import { AppState } from '@core/core.state'; 20 import { AppState } from '@core/core.state';
21 import { TranslateService } from '@ngx-translate/core'; 21 import { TranslateService } from '@ngx-translate/core';
@@ -30,7 +30,7 @@ import { @@ -30,7 +30,7 @@ import {
30 OtaUpdateTypeTranslationMap 30 OtaUpdateTypeTranslationMap
31 } from '@shared/models/ota-package.models'; 31 } from '@shared/models/ota-package.models';
32 import { ActionNotificationShow } from '@core/notification/notification.actions'; 32 import { ActionNotificationShow } from '@core/notification/notification.actions';
33 -import { filter, takeUntil } from 'rxjs/operators'; 33 +import { filter, startWith, takeUntil } from 'rxjs/operators';
34 import { isNotEmptyStr } from '@core/utils'; 34 import { isNotEmptyStr } from '@core/utils';
35 35
36 @Component({ 36 @Component({
@@ -56,22 +56,36 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -56,22 +56,36 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
56 56
57 ngOnInit() { 57 ngOnInit() {
58 super.ngOnInit(); 58 super.ngOnInit();
59 - this.entityForm.get('isURL').valueChanges.pipe(  
60 - filter(() => this.isAdd),  
61 - takeUntil(this.destroy$)  
62 - ).subscribe((isURL) => {  
63 - if (isURL === false) {  
64 - this.entityForm.get('url').clearValidators();  
65 - this.entityForm.get('file').setValidators(Validators.required);  
66 - this.entityForm.get('url').updateValueAndValidity({emitEvent: false});  
67 - this.entityForm.get('file').updateValueAndValidity({emitEvent: false});  
68 - } else {  
69 - this.entityForm.get('file').clearValidators();  
70 - this.entityForm.get('url').setValidators([Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]);  
71 - this.entityForm.get('file').updateValueAndValidity({emitEvent: false});  
72 - this.entityForm.get('url').updateValueAndValidity({emitEvent: false});  
73 - }  
74 - }); 59 + if (this.isAdd) {
  60 + this.entityForm.get('isURL').valueChanges.pipe(
  61 + takeUntil(this.destroy$)
  62 + ).subscribe((isURL) => {
  63 + if (isURL === false) {
  64 + this.entityForm.get('url').clearValidators();
  65 + this.entityForm.get('file').setValidators(Validators.required);
  66 + this.entityForm.get('url').updateValueAndValidity({emitEvent: false});
  67 + this.entityForm.get('file').updateValueAndValidity({emitEvent: false});
  68 + } else {
  69 + this.entityForm.get('file').clearValidators();
  70 + this.entityForm.get('url').setValidators([Validators.required, Validators.pattern('(.|\\s)*\\S(.|\\s)*')]);
  71 + this.entityForm.get('file').updateValueAndValidity({emitEvent: false});
  72 + this.entityForm.get('url').updateValueAndValidity({emitEvent: false});
  73 + }
  74 + });
  75 + combineLatest([
  76 + this.entityForm.get('title').valueChanges.pipe(startWith(''), takeUntil(this.destroy$)),
  77 + this.entityForm.get('version').valueChanges.pipe(startWith(''), takeUntil(this.destroy$))
  78 + ]).pipe(
  79 + filter(() => this.entityForm.get('tag').pristine),
  80 + takeUntil(this.destroy$)
  81 + ).subscribe(([title, version]) => {
  82 + let tag = `${title} ${version}`.trim();
  83 + if (tag === '') {
  84 + tag = '';
  85 + }
  86 + this.entityForm.get('tag').patchValue(tag);
  87 + });
  88 + }
75 } 89 }
76 90
77 ngOnDestroy() { 91 ngOnDestroy() {
@@ -92,6 +106,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -92,6 +106,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
92 const form = this.fb.group({ 106 const form = this.fb.group({
93 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], 107 title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]],
94 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], 108 version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]],
  109 + tag: [entity ? entity.tag : '', [Validators.maxLength(255)]],
95 type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required], 110 type: [entity?.type ? entity.type : OtaUpdateType.FIRMWARE, Validators.required],
96 deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required], 111 deviceProfileId: [entity ? entity.deviceProfileId : null, Validators.required],
97 checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256], 112 checksumAlgorithm: [entity && entity.checksumAlgorithm ? entity.checksumAlgorithm : ChecksumAlgorithm.SHA256],
@@ -119,6 +134,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O @@ -119,6 +134,7 @@ export class OtaUpdateComponent extends EntityComponent<OtaPackage> implements O
119 this.entityForm.patchValue({ 134 this.entityForm.patchValue({
120 title: entity.title, 135 title: entity.title,
121 version: entity.version, 136 version: entity.version,
  137 + tag: entity.tag,
122 type: entity.type, 138 type: entity.type,
123 deviceProfileId: entity.deviceProfileId, 139 deviceProfileId: entity.deviceProfileId,
124 checksumAlgorithm: entity.checksumAlgorithm, 140 checksumAlgorithm: entity.checksumAlgorithm,
@@ -91,6 +91,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> { @@ -91,6 +91,7 @@ export interface OtaPackageInfo extends BaseData<OtaPackageId> {
91 deviceProfileId?: DeviceProfileId; 91 deviceProfileId?: DeviceProfileId;
92 title?: string; 92 title?: string;
93 version?: string; 93 version?: string;
  94 + tag?: string;
94 hasData?: boolean; 95 hasData?: boolean;
95 url?: string; 96 url?: string;
96 fileName: string; 97 fileName: string;
@@ -2342,8 +2342,12 @@ @@ -2342,8 +2342,12 @@
2342 "firmware": "Firmware", 2342 "firmware": "Firmware",
2343 "software": "Software" 2343 "software": "Software"
2344 }, 2344 },
  2345 + "upload-binary-file": "Upload binary file",
  2346 + "use-external-url": "Use external URL",
2345 "version": "Version", 2347 "version": "Version",
2346 "version-required": "Version is required.", 2348 "version-required": "Version is required.",
  2349 + "version-tag": "Version Tag",
  2350 + "version-tag-hint": "Custom tag should match the package version reported by your device.",
2347 "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type." 2351 "warning-after-save-no-edit": "Once the package is uploaded, you will not be able to modify title, version, device profile and package type."
2348 }, 2352 },
2349 "position": { 2353 "position": {