Commit 5976d5172969e0fb11f604686f75bb27a0a7a3eb

Authored by Vladyslav_Prykhodko
Committed by Andrew Shvayka
1 parent 7edcc605

UI: Add import/export tenant profile

@@ -57,6 +57,8 @@ import { RuleChainService } from '@core/http/rule-chain.service'; @@ -57,6 +57,8 @@ import { RuleChainService } from '@core/http/rule-chain.service';
57 import { FiltersInfo } from '@shared/models/query/query.models'; 57 import { FiltersInfo } from '@shared/models/query/query.models';
58 import { DeviceProfileService } from '@core/http/device-profile.service'; 58 import { DeviceProfileService } from '@core/http/device-profile.service';
59 import { DeviceProfile } from '@shared/models/device.models'; 59 import { DeviceProfile } from '@shared/models/device.models';
  60 +import { TenantProfile } from '@shared/models/tenant.model';
  61 +import { TenantProfileService } from '@core/http/tenant-profile.service';
60 62
61 // @dynamic 63 // @dynamic
62 @Injectable() 64 @Injectable()
@@ -70,6 +72,7 @@ export class ImportExportService { @@ -70,6 +72,7 @@ export class ImportExportService {
70 private dashboardUtils: DashboardUtilsService, 72 private dashboardUtils: DashboardUtilsService,
71 private widgetService: WidgetService, 73 private widgetService: WidgetService,
72 private deviceProfileService: DeviceProfileService, 74 private deviceProfileService: DeviceProfileService,
  75 + private tenantProfileService: TenantProfileService,
73 private entityService: EntityService, 76 private entityService: EntityService,
74 private ruleChainService: RuleChainService, 77 private ruleChainService: RuleChainService,
75 private utils: UtilsService, 78 private utils: UtilsService,
@@ -434,7 +437,7 @@ export class ImportExportService { @@ -434,7 +437,7 @@ export class ImportExportService {
434 (deviceProfile) => { 437 (deviceProfile) => {
435 let name = deviceProfile.name; 438 let name = deviceProfile.name;
436 name = name.toLowerCase().replace(/\W/g, '_'); 439 name = name.toLowerCase().replace(/\W/g, '_');
437 - this.exportToPc(this.prepareDeviceProfileExport(deviceProfile), name); 440 + this.exportToPc(this.prepareProfileExport(deviceProfile), name);
438 }, 441 },
439 (e) => { 442 (e) => {
440 this.handleExportError(e, 'device-profile.export-failed-error'); 443 this.handleExportError(e, 'device-profile.export-failed-error');
@@ -460,6 +463,37 @@ export class ImportExportService { @@ -460,6 +463,37 @@ export class ImportExportService {
460 ); 463 );
461 } 464 }
462 465
  466 + public exportTenantProfile(tenantProfileId: string) {
  467 + this.tenantProfileService.getTenantProfile(tenantProfileId).subscribe(
  468 + (tenantProfile) => {
  469 + let name = tenantProfile.name;
  470 + name = name.toLowerCase().replace(/\W/g, '_');
  471 + this.exportToPc(this.prepareProfileExport(tenantProfile), name);
  472 + },
  473 + (e) => {
  474 + this.handleExportError(e, 'tenant-profile.export-failed-error');
  475 + }
  476 + );
  477 + }
  478 +
  479 + public importTenantProfile(): Observable<TenantProfile> {
  480 + return this.openImportDialog('tenant-profile.import', 'tenant-profile.tenant-profile-file').pipe(
  481 + mergeMap((tenantProfile: TenantProfile) => {
  482 + if (!this.validateImportedTenantProfile(tenantProfile)) {
  483 + this.store.dispatch(new ActionNotificationShow(
  484 + {message: this.translate.instant('tenant-profile.invalid-tenant-profile-file-error'),
  485 + type: 'error'}));
  486 + throw new Error('Invalid tenant profile file');
  487 + } else {
  488 + return this.tenantProfileService.saveTenantProfile(tenantProfile);
  489 + }
  490 + }),
  491 + catchError(() => {
  492 + return of(null);
  493 + })
  494 + );
  495 + }
  496 +
463 public exportJSZip(data: object, filename: string) { 497 public exportJSZip(data: object, filename: string) {
464 import('jszip').then((JSZip) => { 498 import('jszip').then((JSZip) => {
465 const jsZip = new JSZip.default(); 499 const jsZip = new JSZip.default();
@@ -517,6 +551,13 @@ export class ImportExportService { @@ -517,6 +551,13 @@ export class ImportExportService {
517 return true; 551 return true;
518 } 552 }
519 553
  554 + private validateImportedTenantProfile(tenantProfile: TenantProfile): boolean {
  555 + return isDefined(tenantProfile.name)
  556 + && isDefined(tenantProfile.profileData)
  557 + && isDefined(tenantProfile.isolatedTbCore)
  558 + && isDefined(tenantProfile.isolatedTbRuleEngine);
  559 + }
  560 +
520 private sumObject(obj1: any, obj2: any): any { 561 private sumObject(obj1: any, obj2: any): any {
521 Object.keys(obj2).map((key) => { 562 Object.keys(obj2).map((key) => {
522 if (isObject(obj2[key])) { 563 if (isObject(obj2[key])) {
@@ -798,10 +839,10 @@ export class ImportExportService { @@ -798,10 +839,10 @@ export class ImportExportService {
798 return dashboard; 839 return dashboard;
799 } 840 }
800 841
801 - private prepareDeviceProfileExport(deviceProfile: DeviceProfile): DeviceProfile {  
802 - deviceProfile = this.prepareExport(deviceProfile);  
803 - deviceProfile.default = false;  
804 - return deviceProfile; 842 + private prepareProfileExport<T extends DeviceProfile|TenantProfile>(profile: T): T {
  843 + profile = this.prepareExport(profile);
  844 + profile.default = false;
  845 + return profile;
805 } 846 }
806 847
807 private prepareExport(data: any): any { 848 private prepareExport(data: any): any {
@@ -21,7 +21,8 @@ import { @@ -21,7 +21,8 @@ import {
21 checkBoxCell, 21 checkBoxCell,
22 DateEntityTableColumn, 22 DateEntityTableColumn,
23 EntityTableColumn, 23 EntityTableColumn,
24 - EntityTableConfig 24 + EntityTableConfig,
  25 + HeaderActionDescriptor
25 } from '@home/models/entity/entities-table-config.models'; 26 } from '@home/models/entity/entities-table-config.models';
26 import { TranslateService } from '@ngx-translate/core'; 27 import { TranslateService } from '@ngx-translate/core';
27 import { DatePipe } from '@angular/common'; 28 import { DatePipe } from '@angular/common';
@@ -31,6 +32,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service'; @@ -31,6 +32,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service';
31 import { TenantProfileComponent } from '../../components/profile/tenant-profile.component'; 32 import { TenantProfileComponent } from '../../components/profile/tenant-profile.component';
32 import { TenantProfileTabsComponent } from './tenant-profile-tabs.component'; 33 import { TenantProfileTabsComponent } from './tenant-profile-tabs.component';
33 import { DialogService } from '@core/services/dialog.service'; 34 import { DialogService } from '@core/services/dialog.service';
  35 +import { ImportExportService } from '@home/components/import-export/import-export.service';
34 36
35 @Injectable() 37 @Injectable()
36 export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> { 38 export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> {
@@ -38,6 +40,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -38,6 +40,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
38 private readonly config: EntityTableConfig<TenantProfile> = new EntityTableConfig<TenantProfile>(); 40 private readonly config: EntityTableConfig<TenantProfile> = new EntityTableConfig<TenantProfile>();
39 41
40 constructor(private tenantProfileService: TenantProfileService, 42 constructor(private tenantProfileService: TenantProfileService,
  43 + private importExport: ImportExportService,
41 private translate: TranslateService, 44 private translate: TranslateService,
42 private datePipe: DatePipe, 45 private datePipe: DatePipe,
43 private dialogService: DialogService) { 46 private dialogService: DialogService) {
@@ -60,6 +63,12 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -60,6 +63,12 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
60 63
61 this.config.cellActionDescriptors.push( 64 this.config.cellActionDescriptors.push(
62 { 65 {
  66 + name: this.translate.instant('tenant-profile.export'),
  67 + icon: 'file_download',
  68 + isEnabled: () => true,
  69 + onAction: ($event, entity) => this.exportTenantProfile($event, entity)
  70 + },
  71 + {
63 name: this.translate.instant('tenant-profile.set-default'), 72 name: this.translate.instant('tenant-profile.set-default'),
64 icon: 'flag', 73 icon: 'flag',
65 isEnabled: (tenantProfile) => !tenantProfile.default, 74 isEnabled: (tenantProfile) => !tenantProfile.default,
@@ -80,6 +89,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -80,6 +89,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
80 this.config.onEntityAction = action => this.onTenantProfileAction(action); 89 this.config.onEntityAction = action => this.onTenantProfileAction(action);
81 this.config.deleteEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default; 90 this.config.deleteEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default;
82 this.config.entitySelectionEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default; 91 this.config.entitySelectionEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default;
  92 + this.config.addActionDescriptors = this.configureAddActions();
83 } 93 }
84 94
85 resolve(): EntityTableConfig<TenantProfile> { 95 resolve(): EntityTableConfig<TenantProfile> {
@@ -88,6 +98,25 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -88,6 +98,25 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
88 return this.config; 98 return this.config;
89 } 99 }
90 100
  101 + configureAddActions(): Array<HeaderActionDescriptor> {
  102 + const actions: Array<HeaderActionDescriptor> = [];
  103 + actions.push(
  104 + {
  105 + name: this.translate.instant('tenant-profile.create-tenant-profile'),
  106 + icon: 'insert_drive_file',
  107 + isEnabled: () => true,
  108 + onAction: ($event) => this.config.table.addEntity($event)
  109 + },
  110 + {
  111 + name: this.translate.instant('tenant-profile.import'),
  112 + icon: 'file_upload',
  113 + isEnabled: () => true,
  114 + onAction: ($event) => this.importTenantProfile($event)
  115 + }
  116 + );
  117 + return actions;
  118 + }
  119 +
91 setDefaultTenantProfile($event: Event, tenantProfile: TenantProfile) { 120 setDefaultTenantProfile($event: Event, tenantProfile: TenantProfile) {
92 if ($event) { 121 if ($event) {
93 $event.stopPropagation(); 122 $event.stopPropagation();
@@ -110,6 +139,23 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon @@ -110,6 +139,23 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
110 ); 139 );
111 } 140 }
112 141
  142 + importTenantProfile($event: Event) {
  143 + this.importExport.importTenantProfile().subscribe(
  144 + (deviceProfile) => {
  145 + if (deviceProfile) {
  146 + this.config.table.updateData();
  147 + }
  148 + }
  149 + );
  150 + }
  151 +
  152 + exportTenantProfile($event: Event, tenantProfile: TenantProfile) {
  153 + if ($event) {
  154 + $event.stopPropagation();
  155 + }
  156 + this.importExport.exportTenantProfile(tenantProfile.id.id);
  157 + }
  158 +
113 onTenantProfileAction(action: EntityAction<TenantProfile>): boolean { 159 onTenantProfileAction(action: EntityAction<TenantProfile>): boolean {
114 switch (action.action) { 160 switch (action.action) {
115 case 'setDefault': 161 case 'setDefault':
@@ -2440,6 +2440,12 @@ @@ -2440,6 +2440,12 @@
2440 "set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified.", 2440 "set-default-tenant-profile-text": "After the confirmation the tenant profile will be marked as default and will be used for new tenants with no profile specified.",
2441 "no-tenant-profiles-found": "No tenant profiles found.", 2441 "no-tenant-profiles-found": "No tenant profiles found.",
2442 "create-new-tenant-profile": "Create a new one!", 2442 "create-new-tenant-profile": "Create a new one!",
  2443 + "create-tenant-profile": "Create new tenant profile",
  2444 + "import": "Import tenant profile",
  2445 + "export": "Export tenant profile",
  2446 + "export-failed-error": "Unable to export tenant profile: {{error}}",
  2447 + "tenant-profile-file": "Tenant profile file",
  2448 + "invalid-tenant-profile-file-error": "Unable to import tenant profile: Invalid tenant profile data structure.",
2443 "maximum-devices": "Maximum number of devices (0 - unlimited)", 2449 "maximum-devices": "Maximum number of devices (0 - unlimited)",
2444 "maximum-devices-required": "Maximum number of devices is required.", 2450 "maximum-devices-required": "Maximum number of devices is required.",
2445 "maximum-devices-range": "Minimum number of devices can't be negative", 2451 "maximum-devices-range": "Minimum number of devices can't be negative",