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 57 import { FiltersInfo } from '@shared/models/query/query.models';
58 58 import { DeviceProfileService } from '@core/http/device-profile.service';
59 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 63 // @dynamic
62 64 @Injectable()
... ... @@ -70,6 +72,7 @@ export class ImportExportService {
70 72 private dashboardUtils: DashboardUtilsService,
71 73 private widgetService: WidgetService,
72 74 private deviceProfileService: DeviceProfileService,
  75 + private tenantProfileService: TenantProfileService,
73 76 private entityService: EntityService,
74 77 private ruleChainService: RuleChainService,
75 78 private utils: UtilsService,
... ... @@ -434,7 +437,7 @@ export class ImportExportService {
434 437 (deviceProfile) => {
435 438 let name = deviceProfile.name;
436 439 name = name.toLowerCase().replace(/\W/g, '_');
437   - this.exportToPc(this.prepareDeviceProfileExport(deviceProfile), name);
  440 + this.exportToPc(this.prepareProfileExport(deviceProfile), name);
438 441 },
439 442 (e) => {
440 443 this.handleExportError(e, 'device-profile.export-failed-error');
... ... @@ -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 497 public exportJSZip(data: object, filename: string) {
464 498 import('jszip').then((JSZip) => {
465 499 const jsZip = new JSZip.default();
... ... @@ -517,6 +551,13 @@ export class ImportExportService {
517 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 561 private sumObject(obj1: any, obj2: any): any {
521 562 Object.keys(obj2).map((key) => {
522 563 if (isObject(obj2[key])) {
... ... @@ -798,10 +839,10 @@ export class ImportExportService {
798 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 848 private prepareExport(data: any): any {
... ...
... ... @@ -21,7 +21,8 @@ import {
21 21 checkBoxCell,
22 22 DateEntityTableColumn,
23 23 EntityTableColumn,
24   - EntityTableConfig
  24 + EntityTableConfig,
  25 + HeaderActionDescriptor
25 26 } from '@home/models/entity/entities-table-config.models';
26 27 import { TranslateService } from '@ngx-translate/core';
27 28 import { DatePipe } from '@angular/common';
... ... @@ -31,6 +32,7 @@ import { TenantProfileService } from '@core/http/tenant-profile.service';
31 32 import { TenantProfileComponent } from '../../components/profile/tenant-profile.component';
32 33 import { TenantProfileTabsComponent } from './tenant-profile-tabs.component';
33 34 import { DialogService } from '@core/services/dialog.service';
  35 +import { ImportExportService } from '@home/components/import-export/import-export.service';
34 36
35 37 @Injectable()
36 38 export class TenantProfilesTableConfigResolver implements Resolve<EntityTableConfig<TenantProfile>> {
... ... @@ -38,6 +40,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
38 40 private readonly config: EntityTableConfig<TenantProfile> = new EntityTableConfig<TenantProfile>();
39 41
40 42 constructor(private tenantProfileService: TenantProfileService,
  43 + private importExport: ImportExportService,
41 44 private translate: TranslateService,
42 45 private datePipe: DatePipe,
43 46 private dialogService: DialogService) {
... ... @@ -60,6 +63,12 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
60 63
61 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 72 name: this.translate.instant('tenant-profile.set-default'),
64 73 icon: 'flag',
65 74 isEnabled: (tenantProfile) => !tenantProfile.default,
... ... @@ -80,6 +89,7 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
80 89 this.config.onEntityAction = action => this.onTenantProfileAction(action);
81 90 this.config.deleteEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default;
82 91 this.config.entitySelectionEnabled = (tenantProfile) => tenantProfile && !tenantProfile.default;
  92 + this.config.addActionDescriptors = this.configureAddActions();
83 93 }
84 94
85 95 resolve(): EntityTableConfig<TenantProfile> {
... ... @@ -88,6 +98,25 @@ export class TenantProfilesTableConfigResolver implements Resolve<EntityTableCon
88 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 120 setDefaultTenantProfile($event: Event, tenantProfile: TenantProfile) {
92 121 if ($event) {
93 122 $event.stopPropagation();
... ... @@ -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 159 onTenantProfileAction(action: EntityAction<TenantProfile>): boolean {
114 160 switch (action.action) {
115 161 case 'setDefault':
... ...
... ... @@ -2440,6 +2440,12 @@
2440 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 2441 "no-tenant-profiles-found": "No tenant profiles found.",
2442 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 2449 "maximum-devices": "Maximum number of devices (0 - unlimited)",
2444 2450 "maximum-devices-required": "Maximum number of devices is required.",
2445 2451 "maximum-devices-range": "Minimum number of devices can't be negative",
... ...