Commit 5976d5172969e0fb11f604686f75bb27a0a7a3eb
Committed by
Andrew Shvayka
1 parent
7edcc605
UI: Add import/export tenant profile
Showing
3 changed files
with
99 additions
and
6 deletions
@@ -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", |