diff --git a/ui-ngx/src/app/core/http/asset.service.ts b/ui-ngx/src/app/core/http/asset.service.ts index aa4bb161d1..e106600c57 100644 --- a/ui-ngx/src/app/core/http/asset.service.ts +++ b/ui-ngx/src/app/core/http/asset.service.ts @@ -22,6 +22,7 @@ import { PageLink } from '@shared/models/page/page-link'; import { PageData } from '@shared/models/page/page-data'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; import { Asset, AssetInfo, AssetSearchQuery } from '@app/shared/models/asset.models'; +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; @Injectable({ providedIn: 'root' @@ -105,4 +106,8 @@ export class AssetService { defaultHttpOptionsFromConfig(config)); } + public bulkImportAssets(entitiesData: BulkImportRequest, config?: RequestConfig): Observable { + return this.http.post('/api/asset/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config)); + } + } diff --git a/ui-ngx/src/app/core/http/device.service.ts b/ui-ngx/src/app/core/http/device.service.ts index 04cf633e53..21bd04b39d 100644 --- a/ui-ngx/src/app/core/http/device.service.ts +++ b/ui-ngx/src/app/core/http/device.service.ts @@ -30,6 +30,7 @@ import { } from '@app/shared/models/device.models'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; import { AuthService } from '@core/auth/auth.service'; +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; @Injectable({ providedIn: 'root' @@ -170,7 +171,11 @@ export class DeviceService { public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '', config?: RequestConfig): Observable> { return this.http.get>(`/api/edge/${edgeId}/devices${pageLink.toQuery()}&type=${type}`, - defaultHttpOptionsFromConfig(config)) + defaultHttpOptionsFromConfig(config)); + } + + public bulkImportDevices(entitiesData: BulkImportRequest, config?: RequestConfig): Observable { + return this.http.post('/api/device/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config)); } } diff --git a/ui-ngx/src/app/core/http/edge.service.ts b/ui-ngx/src/app/core/http/edge.service.ts index 7e4974aec4..9bec9b873c 100644 --- a/ui-ngx/src/app/core/http/edge.service.ts +++ b/ui-ngx/src/app/core/http/edge.service.ts @@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data'; import { EntitySubtype } from '@app/shared/models/entity-type.models'; import { Edge, EdgeEvent, EdgeInfo, EdgeSearchQuery } from '@shared/models/edge.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models'; @Injectable({ providedIn: 'root' @@ -59,7 +60,7 @@ export class EdgeService { } public getCustomerEdgeInfos(customerId: string, pageLink: PageLink, type: string = '', - config?: RequestConfig): Observable> { + config?: RequestConfig): Observable> { return this.http.get>(`/api/customer/${customerId}/edgeInfos${pageLink.toQuery()}&type=${type}`, defaultHttpOptionsFromConfig(config)); } @@ -108,4 +109,8 @@ export class EdgeService { public findByName(edgeName: string, config?: RequestConfig): Observable { return this.http.get(`/api/tenant/edges?edgeName=${edgeName}`, defaultHttpOptionsFromConfig(config)); } + + public bulkImportEdges(entitiesData: BulkImportRequest, config?: RequestConfig): Observable { + return this.http.post('/api/edge/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config)); + } } diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html b/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html index 79317b4e3a..035e43690c 100644 --- a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html +++ b/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.html @@ -113,23 +113,23 @@ {{ 'import.stepper-text.creat-entities' | translate }} - + {{ 'import.stepper-text.done' | translate }}
-

- {{ translate.instant('import.message.create-entities', {count: this.statistical.create.entity}) }} +

+ {{ translate.instant('import.message.create-entities', {count: this.statistical.created}) }}

-

- {{ translate.instant('import.message.update-entities', {count: this.statistical.update.entity}) }} +

+ {{ translate.instant('import.message.update-entities', {count: this.statistical.updated}) }}

-

- {{ translate.instant('import.message.error-entities', {count: this.statistical.error.entity}) }} +

+ {{ translate.instant('import.message.error-entities', {count: this.statistical.errors}) }}

diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts b/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts index fa9b09258a..29ab0857de 100644 --- a/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts +++ b/ui-ngx/src/app/modules/home/components/import-export/import-dialog-csv.component.ts @@ -26,18 +26,18 @@ import { TranslateService } from '@ngx-translate/core'; import { ActionNotificationShow } from '@core/notification/notification.actions'; import { MatVerticalStepper } from '@angular/material/stepper'; import { + BulkImportRequest, + BulkImportResult, + ColumnMapping, convertCSVToJson, CsvColumnParam, + CSVDelimiter, CsvToJsonConfig, CsvToJsonResult, ImportEntityColumnType } from '@home/components/import-export/import-export.models'; -import { EdgeImportEntityData, ImportEntitiesResultInfo, ImportEntityData } from '@app/shared/models/entity.models'; import { ImportExportService } from '@home/components/import-export/import-export.service'; import { TableColumnsAssignmentComponent } from '@home/components/import-export/table-columns-assignment.component'; -import { getDeviceCredentialMQTTDefault } from '@shared/models/device.models'; -import { isDefinedAndNotNull } from '@core/utils'; -import { getLwm2mSecurityConfigModelsDefault } from '@shared/models/lwm2m-security-config.models'; import { Ace } from 'ace-builds'; import { getAce } from '@shared/models/ace/ace.models'; @@ -68,7 +68,7 @@ export class ImportDialogCsvComponent extends DialogComponent { - sentDataLength++; - this.progressCreate = Math.round((sentDataLength / importData.rows.length) * 100); }; - - const isUpdate: boolean = this.importParametersFormGroup.get('isUpdate').value; - - this.importExport.importEntities(entitiesData, this.entityType, isUpdate, - createImportEntityCompleted, {ignoreErrors: true, resendRequest: true}).subscribe( + this.importExport.bulkImportEntities(entitiesData, this.entityType, {ignoreErrors: true}).subscribe( (result) => { this.statistical = result; this.isImportData = false; @@ -386,36 +232,22 @@ export class ImportDialogCsvComponent extends DialogComponent { + const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value; + const allowKeyForTypeColumns: ImportEntityColumnType[] = [ + ImportEntityColumnType.serverAttribute, + ImportEntityColumnType.timeseries, + ImportEntityColumnType.sharedAttribute + ]; + return parameterColumns.map(column => ({ + type: column.type, + key: allowKeyForTypeColumns.some(type => type === column.type) ? column.key : undefined + })); } initEditor() { if (!this.initEditorComponent) { - this.createEditor(this.failureDetailsEditorElmRef, this.statistical.error.errors); + this.createEditor(this.failureDetailsEditorElmRef, this.statistical.errorsList.join('\n')); } } diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts b/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts index bd0f1c6b81..c12b530b2a 100644 --- a/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts +++ b/ui-ngx/src/app/modules/home/components/import-export/import-export.models.ts @@ -38,6 +38,8 @@ export interface CsvToJsonResult { rows?: any[][]; } +export type CSVDelimiter = ',' | ';' | '|' | '\t'; + export enum ImportEntityColumnType { name = 'NAME', type = 'TYPE', @@ -113,6 +115,28 @@ export interface CsvColumnParam { sampleData: any; } +export interface ColumnMapping { + type: ImportEntityColumnType; + key?: string; +} + +export interface BulkImportRequest { + file: string; + mapping: { + columns: Array; + delimiter: CSVDelimiter; + header: boolean; + update: boolean; + }; +} + +export interface BulkImportResult { + created: number; + updated: number; + errors: number; + errorsList: Array; +} + export interface FileType { mimeType: string; extension: string; diff --git a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts b/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts index 3bff936da1..9044b5e2fc 100644 --- a/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts +++ b/ui-ngx/src/app/modules/home/components/import-export/import-export.service.ts @@ -44,7 +44,15 @@ import { EntityAliasesDialogData } from '@home/components/alias/entity-aliases-dialog.component'; import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service'; -import { FileType, ImportWidgetResult, JSON_TYPE, WidgetsBundleItem, ZIP_TYPE } from './import-export.models'; +import { + BulkImportRequest, + BulkImportResult, + FileType, + ImportWidgetResult, + JSON_TYPE, + WidgetsBundleItem, + ZIP_TYPE +} from './import-export.models'; import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; import { UtilsService } from '@core/services/utils.service'; import { WidgetService } from '@core/http/widget.service'; @@ -59,6 +67,9 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; import { DeviceProfile } from '@shared/models/device.models'; import { TenantProfile } from '@shared/models/tenant.model'; import { TenantProfileService } from '@core/http/tenant-profile.service'; +import { DeviceService } from '@core/http/device.service'; +import { AssetService } from '@core/http/asset.service'; +import { EdgeService } from '@core/http/edge.service'; // @dynamic @Injectable() @@ -75,6 +86,9 @@ export class ImportExportService { private tenantProfileService: TenantProfileService, private entityService: EntityService, private ruleChainService: RuleChainService, + private deviceService: DeviceService, + private assetService: AssetService, + private edgeService: EdgeService, private utils: UtilsService, private itembuffer: ItemBufferService, private dialog: MatDialog) { @@ -342,6 +356,17 @@ export class ImportExportService { ); } + public bulkImportEntities(entitiesData: BulkImportRequest, entityType: EntityType, config?: RequestConfig): Observable { + switch (entityType) { + case EntityType.DEVICE: + return this.deviceService.bulkImportDevices(entitiesData, config); + case EntityType.ASSET: + return this.assetService.bulkImportAssets(entitiesData, config); + case EntityType.EDGE: + return this.edgeService.bulkImportEdges(entitiesData, config); + } + } + public importEntities(entitiesData: ImportEntityData[], entityType: EntityType, updateData: boolean, importEntityCompleted?: () => void, config?: RequestConfig): Observable { let partSize = 100;