Commit 2696c1b8ec4cbfd095eae050bc330b2b5c9d690c

Authored by Vladyslav_Prykhodko
1 parent 41e57249

UI: Implementation of new endpoint for bulk import

@@ -22,6 +22,7 @@ import { PageLink } from '@shared/models/page/page-link'; @@ -22,6 +22,7 @@ import { PageLink } from '@shared/models/page/page-link';
22 import { PageData } from '@shared/models/page/page-data'; 22 import { PageData } from '@shared/models/page/page-data';
23 import { EntitySubtype } from '@app/shared/models/entity-type.models'; 23 import { EntitySubtype } from '@app/shared/models/entity-type.models';
24 import { Asset, AssetInfo, AssetSearchQuery } from '@app/shared/models/asset.models'; 24 import { Asset, AssetInfo, AssetSearchQuery } from '@app/shared/models/asset.models';
  25 +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models';
25 26
26 @Injectable({ 27 @Injectable({
27 providedIn: 'root' 28 providedIn: 'root'
@@ -105,4 +106,8 @@ export class AssetService { @@ -105,4 +106,8 @@ export class AssetService {
105 defaultHttpOptionsFromConfig(config)); 106 defaultHttpOptionsFromConfig(config));
106 } 107 }
107 108
  109 + public bulkImportAssets(entitiesData: BulkImportRequest, config?: RequestConfig): Observable<BulkImportResult> {
  110 + return this.http.post<BulkImportResult>('/api/asset/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config));
  111 + }
  112 +
108 } 113 }
@@ -30,6 +30,7 @@ import { @@ -30,6 +30,7 @@ import {
30 } from '@app/shared/models/device.models'; 30 } from '@app/shared/models/device.models';
31 import { EntitySubtype } from '@app/shared/models/entity-type.models'; 31 import { EntitySubtype } from '@app/shared/models/entity-type.models';
32 import { AuthService } from '@core/auth/auth.service'; 32 import { AuthService } from '@core/auth/auth.service';
  33 +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models';
33 34
34 @Injectable({ 35 @Injectable({
35 providedIn: 'root' 36 providedIn: 'root'
@@ -170,7 +171,11 @@ export class DeviceService { @@ -170,7 +171,11 @@ export class DeviceService {
170 public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '', 171 public getEdgeDevices(edgeId: string, pageLink: PageLink, type: string = '',
171 config?: RequestConfig): Observable<PageData<DeviceInfo>> { 172 config?: RequestConfig): Observable<PageData<DeviceInfo>> {
172 return this.http.get<PageData<DeviceInfo>>(`/api/edge/${edgeId}/devices${pageLink.toQuery()}&type=${type}`, 173 return this.http.get<PageData<DeviceInfo>>(`/api/edge/${edgeId}/devices${pageLink.toQuery()}&type=${type}`,
173 - defaultHttpOptionsFromConfig(config)) 174 + defaultHttpOptionsFromConfig(config));
  175 + }
  176 +
  177 + public bulkImportDevices(entitiesData: BulkImportRequest, config?: RequestConfig): Observable<BulkImportResult> {
  178 + return this.http.post<BulkImportResult>('/api/device/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config));
174 } 179 }
175 180
176 } 181 }
@@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data'; @@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data';
23 import { EntitySubtype } from '@app/shared/models/entity-type.models'; 23 import { EntitySubtype } from '@app/shared/models/entity-type.models';
24 import { Edge, EdgeEvent, EdgeInfo, EdgeSearchQuery } from '@shared/models/edge.models'; 24 import { Edge, EdgeEvent, EdgeInfo, EdgeSearchQuery } from '@shared/models/edge.models';
25 import { EntityId } from '@shared/models/id/entity-id'; 25 import { EntityId } from '@shared/models/id/entity-id';
  26 +import { BulkImportRequest, BulkImportResult } from '@home/components/import-export/import-export.models';
26 27
27 @Injectable({ 28 @Injectable({
28 providedIn: 'root' 29 providedIn: 'root'
@@ -59,7 +60,7 @@ export class EdgeService { @@ -59,7 +60,7 @@ export class EdgeService {
59 } 60 }
60 61
61 public getCustomerEdgeInfos(customerId: string, pageLink: PageLink, type: string = '', 62 public getCustomerEdgeInfos(customerId: string, pageLink: PageLink, type: string = '',
62 - config?: RequestConfig): Observable<PageData<EdgeInfo>> { 63 + config?: RequestConfig): Observable<PageData<EdgeInfo>> {
63 return this.http.get<PageData<EdgeInfo>>(`/api/customer/${customerId}/edgeInfos${pageLink.toQuery()}&type=${type}`, 64 return this.http.get<PageData<EdgeInfo>>(`/api/customer/${customerId}/edgeInfos${pageLink.toQuery()}&type=${type}`,
64 defaultHttpOptionsFromConfig(config)); 65 defaultHttpOptionsFromConfig(config));
65 } 66 }
@@ -108,4 +109,8 @@ export class EdgeService { @@ -108,4 +109,8 @@ export class EdgeService {
108 public findByName(edgeName: string, config?: RequestConfig): Observable<Edge> { 109 public findByName(edgeName: string, config?: RequestConfig): Observable<Edge> {
109 return this.http.get<Edge>(`/api/tenant/edges?edgeName=${edgeName}`, defaultHttpOptionsFromConfig(config)); 110 return this.http.get<Edge>(`/api/tenant/edges?edgeName=${edgeName}`, defaultHttpOptionsFromConfig(config));
110 } 111 }
  112 +
  113 + public bulkImportEdges(entitiesData: BulkImportRequest, config?: RequestConfig): Observable<BulkImportResult> {
  114 + return this.http.post<BulkImportResult>('/api/edge/bulk_import', entitiesData, defaultHttpOptionsFromConfig(config));
  115 + }
111 } 116 }
@@ -113,23 +113,23 @@ @@ -113,23 +113,23 @@
113 </mat-step> 113 </mat-step>
114 <mat-step> 114 <mat-step>
115 <ng-template matStepLabel>{{ 'import.stepper-text.creat-entities' | translate }}</ng-template> 115 <ng-template matStepLabel>{{ 'import.stepper-text.creat-entities' | translate }}</ng-template>
116 - <mat-progress-bar color="warn" class="tb-import-progress" mode="determinate" [value]="progressCreate"> 116 + <mat-progress-bar color="warn" class="tb-import-progress" mode="indeterminate">
117 </mat-progress-bar> 117 </mat-progress-bar>
118 </mat-step> 118 </mat-step>
119 <mat-step> 119 <mat-step>
120 <ng-template matStepLabel>{{ 'import.stepper-text.done' | translate }}</ng-template> 120 <ng-template matStepLabel>{{ 'import.stepper-text.done' | translate }}</ng-template>
121 <div fxLayout="column"> 121 <div fxLayout="column">
122 - <p class="mat-body-1" *ngIf="this.statistical?.create && this.statistical?.create.entity">  
123 - {{ translate.instant('import.message.create-entities', {count: this.statistical.create.entity}) }} 122 + <p class="mat-body-1" *ngIf="this.statistical?.created">
  123 + {{ translate.instant('import.message.create-entities', {count: this.statistical.created}) }}
124 </p> 124 </p>
125 - <p class="mat-body-1" *ngIf="this.statistical?.update && this.statistical?.update.entity">  
126 - {{ translate.instant('import.message.update-entities', {count: this.statistical.update.entity}) }} 125 + <p class="mat-body-1" *ngIf="this.statistical?.updated">
  126 + {{ translate.instant('import.message.update-entities', {count: this.statistical.updated}) }}
127 </p> 127 </p>
128 - <p class="mat-body-1" style="margin-bottom: 0.8em" *ngIf="this.statistical?.error && this.statistical?.error.entity">  
129 - {{ translate.instant('import.message.error-entities', {count: this.statistical.error.entity}) }} 128 + <p class="mat-body-1" style="margin-bottom: 0.8em" *ngIf="this.statistical?.errors">
  129 + {{ translate.instant('import.message.error-entities', {count: this.statistical.errors}) }}
130 </p> 130 </p>
131 <mat-expansion-panel class="advanced-logs" [expanded]="false" 131 <mat-expansion-panel class="advanced-logs" [expanded]="false"
132 - *ngIf="this.statistical?.error && this.statistical?.error.entity" 132 + *ngIf="this.statistical?.errorsList?.length"
133 (opened)="initEditor()"> 133 (opened)="initEditor()">
134 <mat-expansion-panel-header [collapsedHeight]="'38px'" [expandedHeight]="'38px'"> 134 <mat-expansion-panel-header [collapsedHeight]="'38px'" [expandedHeight]="'38px'">
135 <mat-panel-title> 135 <mat-panel-title>
@@ -26,18 +26,18 @@ import { TranslateService } from '@ngx-translate/core'; @@ -26,18 +26,18 @@ import { TranslateService } from '@ngx-translate/core';
26 import { ActionNotificationShow } from '@core/notification/notification.actions'; 26 import { ActionNotificationShow } from '@core/notification/notification.actions';
27 import { MatVerticalStepper } from '@angular/material/stepper'; 27 import { MatVerticalStepper } from '@angular/material/stepper';
28 import { 28 import {
  29 + BulkImportRequest,
  30 + BulkImportResult,
  31 + ColumnMapping,
29 convertCSVToJson, 32 convertCSVToJson,
30 CsvColumnParam, 33 CsvColumnParam,
  34 + CSVDelimiter,
31 CsvToJsonConfig, 35 CsvToJsonConfig,
32 CsvToJsonResult, 36 CsvToJsonResult,
33 ImportEntityColumnType 37 ImportEntityColumnType
34 } from '@home/components/import-export/import-export.models'; 38 } from '@home/components/import-export/import-export.models';
35 -import { EdgeImportEntityData, ImportEntitiesResultInfo, ImportEntityData } from '@app/shared/models/entity.models';  
36 import { ImportExportService } from '@home/components/import-export/import-export.service'; 39 import { ImportExportService } from '@home/components/import-export/import-export.service';
37 import { TableColumnsAssignmentComponent } from '@home/components/import-export/table-columns-assignment.component'; 40 import { TableColumnsAssignmentComponent } from '@home/components/import-export/table-columns-assignment.component';
38 -import { getDeviceCredentialMQTTDefault } from '@shared/models/device.models';  
39 -import { isDefinedAndNotNull } from '@core/utils';  
40 -import { getLwm2mSecurityConfigModelsDefault } from '@shared/models/lwm2m-security-config.models';  
41 import { Ace } from 'ace-builds'; 41 import { Ace } from 'ace-builds';
42 import { getAce } from '@shared/models/ace/ace.models'; 42 import { getAce } from '@shared/models/ace/ace.models';
43 43
@@ -68,7 +68,7 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom @@ -68,7 +68,7 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
68 importTitle: string; 68 importTitle: string;
69 importFileLabel: string; 69 importFileLabel: string;
70 70
71 - delimiters: { key: string, value: string }[] = [{ 71 + delimiters: { key: CSVDelimiter, value: string }[] = [{
72 key: ',', 72 key: ',',
73 value: ',' 73 value: ','
74 }, { 74 }, {
@@ -89,8 +89,7 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom @@ -89,8 +89,7 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
89 columnTypesFormGroup: FormGroup; 89 columnTypesFormGroup: FormGroup;
90 90
91 isImportData = false; 91 isImportData = false;
92 - progressCreate = 0;  
93 - statistical: ImportEntitiesResultInfo; 92 + statistical: BulkImportResult;
94 93
95 private allowAssignColumn: ImportEntityColumnType[]; 94 private allowAssignColumn: ImportEntityColumnType[];
96 private initEditorComponent = false; 95 private initEditorComponent = false;
@@ -215,169 +214,16 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom @@ -215,169 +214,16 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
215 214
216 215
217 private addEntities() { 216 private addEntities() {
218 - const importData = this.parseData;  
219 - const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value;  
220 - const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value;  
221 - const entitiesData: ImportEntityData[] = [];  
222 - let sentDataLength = 0;  
223 - const startLineNumber = isHeader ? 2 : 1;  
224 - for (let row = 0; row < importData.rows.length; row++) {  
225 - const entityData: ImportEntityData = this.constructDraftImportEntityData();  
226 - const i = row;  
227 - entityData.lineNumber = startLineNumber + i;  
228 - for (let j = 0; j < parameterColumns.length; j++) {  
229 - if (!isDefinedAndNotNull(importData.rows[i][j]) || importData.rows[i][j] === '') {  
230 - continue;  
231 - }  
232 - switch (parameterColumns[j].type) {  
233 - case ImportEntityColumnType.serverAttribute:  
234 - entityData.attributes.server.push({  
235 - key: parameterColumns[j].key,  
236 - value: importData.rows[i][j]  
237 - });  
238 - break;  
239 - case ImportEntityColumnType.timeseries:  
240 - entityData.timeseries.push({  
241 - key: parameterColumns[j].key,  
242 - value: importData.rows[i][j]  
243 - });  
244 - break;  
245 - case ImportEntityColumnType.sharedAttribute:  
246 - entityData.attributes.shared.push({  
247 - key: parameterColumns[j].key,  
248 - value: importData.rows[i][j]  
249 - });  
250 - break;  
251 - case ImportEntityColumnType.name:  
252 - entityData.name = importData.rows[i][j];  
253 - break;  
254 - case ImportEntityColumnType.type:  
255 - entityData.type = importData.rows[i][j];  
256 - break;  
257 - case ImportEntityColumnType.label:  
258 - entityData.label = importData.rows[i][j];  
259 - break;  
260 - case ImportEntityColumnType.isGateway:  
261 - entityData.gateway = importData.rows[i][j];  
262 - break;  
263 - case ImportEntityColumnType.description:  
264 - entityData.description = importData.rows[i][j];  
265 - break;  
266 - case ImportEntityColumnType.accessToken:  
267 - entityData.credential.accessToken = importData.rows[i][j];  
268 - break;  
269 - case ImportEntityColumnType.x509:  
270 - entityData.credential.x509 = importData.rows[i][j];  
271 - break;  
272 - case ImportEntityColumnType.mqttClientId:  
273 - if (!entityData.credential.mqtt) {  
274 - entityData.credential.mqtt = getDeviceCredentialMQTTDefault();  
275 - }  
276 - entityData.credential.mqtt.clientId = importData.rows[i][j];  
277 - break;  
278 - case ImportEntityColumnType.mqttUserName:  
279 - if (!entityData.credential.mqtt) {  
280 - entityData.credential.mqtt = getDeviceCredentialMQTTDefault();  
281 - }  
282 - entityData.credential.mqtt.userName = importData.rows[i][j];  
283 - break;  
284 - case ImportEntityColumnType.mqttPassword:  
285 - if (!entityData.credential.mqtt) {  
286 - entityData.credential.mqtt = getDeviceCredentialMQTTDefault();  
287 - }  
288 - entityData.credential.mqtt.password = importData.rows[i][j];  
289 - break;  
290 - case ImportEntityColumnType.lwm2mClientEndpoint:  
291 - if (!entityData.credential.lwm2m) {  
292 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
293 - }  
294 - entityData.credential.lwm2m.client.endpoint = importData.rows[i][j];  
295 - break;  
296 - case ImportEntityColumnType.lwm2mClientSecurityConfigMode:  
297 - if (!entityData.credential.lwm2m) {  
298 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
299 - }  
300 - entityData.credential.lwm2m.client.securityConfigClientMode = importData.rows[i][j];  
301 - break;  
302 - case ImportEntityColumnType.lwm2mClientIdentity:  
303 - if (!entityData.credential.lwm2m) {  
304 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
305 - }  
306 - entityData.credential.lwm2m.client.identity = importData.rows[i][j];  
307 - break;  
308 - case ImportEntityColumnType.lwm2mClientKey:  
309 - if (!entityData.credential.lwm2m) {  
310 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
311 - }  
312 - entityData.credential.lwm2m.client.key = importData.rows[i][j];  
313 - break;  
314 - case ImportEntityColumnType.lwm2mClientCert:  
315 - if (!entityData.credential.lwm2m) {  
316 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
317 - }  
318 - entityData.credential.lwm2m.client.cert = importData.rows[i][j];  
319 - break;  
320 - case ImportEntityColumnType.lwm2mBootstrapServerSecurityMode:  
321 - if (!entityData.credential.lwm2m) {  
322 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
323 - }  
324 - entityData.credential.lwm2m.bootstrap.bootstrapServer.securityMode = importData.rows[i][j];  
325 - break;  
326 - case ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId:  
327 - if (!entityData.credential.lwm2m) {  
328 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
329 - }  
330 - entityData.credential.lwm2m.bootstrap.bootstrapServer.clientPublicKeyOrId = importData.rows[i][j];  
331 - break;  
332 - case ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey:  
333 - if (!entityData.credential.lwm2m) {  
334 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
335 - }  
336 - entityData.credential.lwm2m.bootstrap.bootstrapServer.clientSecretKey = importData.rows[i][j];  
337 - break;  
338 - case ImportEntityColumnType.lwm2mServerSecurityMode:  
339 - if (!entityData.credential.lwm2m) {  
340 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
341 - }  
342 - entityData.credential.lwm2m.bootstrap.lwm2mServer.securityMode = importData.rows[i][j];  
343 - break;  
344 - case ImportEntityColumnType.lwm2mServerClientPublicKeyOrId:  
345 - if (!entityData.credential.lwm2m) {  
346 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
347 - }  
348 - entityData.credential.lwm2m.bootstrap.lwm2mServer.clientPublicKeyOrId = importData.rows[i][j];  
349 - break;  
350 - case ImportEntityColumnType.lwm2mServerClientSecretKey:  
351 - if (!entityData.credential.lwm2m) {  
352 - entityData.credential.lwm2m = getLwm2mSecurityConfigModelsDefault();  
353 - }  
354 - entityData.credential.lwm2m.bootstrap.lwm2mServer.clientSecretKey = importData.rows[i][j];  
355 - break;  
356 - case ImportEntityColumnType.edgeLicenseKey:  
357 - (entityData as EdgeImportEntityData).edgeLicenseKey = importData.rows[i][j];  
358 - break;  
359 - case ImportEntityColumnType.cloudEndpoint:  
360 - (entityData as EdgeImportEntityData).cloudEndpoint = importData.rows[i][j];  
361 - break;  
362 - case ImportEntityColumnType.routingKey:  
363 - (entityData as EdgeImportEntityData).routingKey = importData.rows[i][j];  
364 - break;  
365 - case ImportEntityColumnType.secret:  
366 - (entityData as EdgeImportEntityData).secret = importData.rows[i][j];  
367 - break;  
368 - } 217 + const entitiesData: BulkImportRequest = {
  218 + file: this.selectFileFormGroup.get('importData').value,
  219 + mapping: {
  220 + columns: this.processingColumnsParams(),
  221 + delimiter: this.importParametersFormGroup.get('delim').value,
  222 + header: this.importParametersFormGroup.get('isHeader').value,
  223 + update: this.importParametersFormGroup.get('isUpdate').value
369 } 224 }
370 - entitiesData.push(entityData);  
371 - }  
372 - const createImportEntityCompleted = () => {  
373 - sentDataLength++;  
374 - this.progressCreate = Math.round((sentDataLength / importData.rows.length) * 100);  
375 }; 225 };
376 -  
377 - const isUpdate: boolean = this.importParametersFormGroup.get('isUpdate').value;  
378 -  
379 - this.importExport.importEntities(entitiesData, this.entityType, isUpdate,  
380 - createImportEntityCompleted, {ignoreErrors: true, resendRequest: true}).subscribe( 226 + this.importExport.bulkImportEntities(entitiesData, this.entityType, {ignoreErrors: true}).subscribe(
381 (result) => { 227 (result) => {
382 this.statistical = result; 228 this.statistical = result;
383 this.isImportData = false; 229 this.isImportData = false;
@@ -386,36 +232,22 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom @@ -386,36 +232,22 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom
386 ); 232 );
387 } 233 }
388 234
389 - private constructDraftImportEntityData(): ImportEntityData {  
390 - const entityData: ImportEntityData = {  
391 - lineNumber: 1,  
392 - name: '',  
393 - type: '',  
394 - description: '',  
395 - gateway: null,  
396 - label: '',  
397 - attributes: {  
398 - server: [],  
399 - shared: []  
400 - },  
401 - credential: {},  
402 - timeseries: []  
403 - };  
404 - if (this.entityType === EntityType.EDGE) {  
405 - const edgeEntityData: EdgeImportEntityData = entityData as EdgeImportEntityData;  
406 - edgeEntityData.edgeLicenseKey = '';  
407 - edgeEntityData.cloudEndpoint = '';  
408 - edgeEntityData.routingKey = '';  
409 - edgeEntityData.secret = '';  
410 - return edgeEntityData;  
411 - } else {  
412 - return entityData;  
413 - } 235 + private processingColumnsParams(): Array<ColumnMapping> {
  236 + const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value;
  237 + const allowKeyForTypeColumns: ImportEntityColumnType[] = [
  238 + ImportEntityColumnType.serverAttribute,
  239 + ImportEntityColumnType.timeseries,
  240 + ImportEntityColumnType.sharedAttribute
  241 + ];
  242 + return parameterColumns.map(column => ({
  243 + type: column.type,
  244 + key: allowKeyForTypeColumns.some(type => type === column.type) ? column.key : undefined
  245 + }));
414 } 246 }
415 247
416 initEditor() { 248 initEditor() {
417 if (!this.initEditorComponent) { 249 if (!this.initEditorComponent) {
418 - this.createEditor(this.failureDetailsEditorElmRef, this.statistical.error.errors); 250 + this.createEditor(this.failureDetailsEditorElmRef, this.statistical.errorsList.join('\n'));
419 } 251 }
420 } 252 }
421 253
@@ -38,6 +38,8 @@ export interface CsvToJsonResult { @@ -38,6 +38,8 @@ export interface CsvToJsonResult {
38 rows?: any[][]; 38 rows?: any[][];
39 } 39 }
40 40
  41 +export type CSVDelimiter = ',' | ';' | '|' | '\t';
  42 +
41 export enum ImportEntityColumnType { 43 export enum ImportEntityColumnType {
42 name = 'NAME', 44 name = 'NAME',
43 type = 'TYPE', 45 type = 'TYPE',
@@ -113,6 +115,28 @@ export interface CsvColumnParam { @@ -113,6 +115,28 @@ export interface CsvColumnParam {
113 sampleData: any; 115 sampleData: any;
114 } 116 }
115 117
  118 +export interface ColumnMapping {
  119 + type: ImportEntityColumnType;
  120 + key?: string;
  121 +}
  122 +
  123 +export interface BulkImportRequest {
  124 + file: string;
  125 + mapping: {
  126 + columns: Array<ColumnMapping>;
  127 + delimiter: CSVDelimiter;
  128 + header: boolean;
  129 + update: boolean;
  130 + };
  131 +}
  132 +
  133 +export interface BulkImportResult {
  134 + created: number;
  135 + updated: number;
  136 + errors: number;
  137 + errorsList: Array<string>;
  138 +}
  139 +
116 export interface FileType { 140 export interface FileType {
117 mimeType: string; 141 mimeType: string;
118 extension: string; 142 extension: string;
@@ -44,7 +44,15 @@ import { @@ -44,7 +44,15 @@ import {
44 EntityAliasesDialogData 44 EntityAliasesDialogData
45 } from '@home/components/alias/entity-aliases-dialog.component'; 45 } from '@home/components/alias/entity-aliases-dialog.component';
46 import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service'; 46 import { ItemBufferService, WidgetItem } from '@core/services/item-buffer.service';
47 -import { FileType, ImportWidgetResult, JSON_TYPE, WidgetsBundleItem, ZIP_TYPE } from './import-export.models'; 47 +import {
  48 + BulkImportRequest,
  49 + BulkImportResult,
  50 + FileType,
  51 + ImportWidgetResult,
  52 + JSON_TYPE,
  53 + WidgetsBundleItem,
  54 + ZIP_TYPE
  55 +} from './import-export.models';
48 import { AliasEntityType, EntityType } from '@shared/models/entity-type.models'; 56 import { AliasEntityType, EntityType } from '@shared/models/entity-type.models';
49 import { UtilsService } from '@core/services/utils.service'; 57 import { UtilsService } from '@core/services/utils.service';
50 import { WidgetService } from '@core/http/widget.service'; 58 import { WidgetService } from '@core/http/widget.service';
@@ -59,6 +67,9 @@ import { DeviceProfileService } from '@core/http/device-profile.service'; @@ -59,6 +67,9 @@ import { DeviceProfileService } from '@core/http/device-profile.service';
59 import { DeviceProfile } from '@shared/models/device.models'; 67 import { DeviceProfile } from '@shared/models/device.models';
60 import { TenantProfile } from '@shared/models/tenant.model'; 68 import { TenantProfile } from '@shared/models/tenant.model';
61 import { TenantProfileService } from '@core/http/tenant-profile.service'; 69 import { TenantProfileService } from '@core/http/tenant-profile.service';
  70 +import { DeviceService } from '@core/http/device.service';
  71 +import { AssetService } from '@core/http/asset.service';
  72 +import { EdgeService } from '@core/http/edge.service';
62 73
63 // @dynamic 74 // @dynamic
64 @Injectable() 75 @Injectable()
@@ -75,6 +86,9 @@ export class ImportExportService { @@ -75,6 +86,9 @@ export class ImportExportService {
75 private tenantProfileService: TenantProfileService, 86 private tenantProfileService: TenantProfileService,
76 private entityService: EntityService, 87 private entityService: EntityService,
77 private ruleChainService: RuleChainService, 88 private ruleChainService: RuleChainService,
  89 + private deviceService: DeviceService,
  90 + private assetService: AssetService,
  91 + private edgeService: EdgeService,
78 private utils: UtilsService, 92 private utils: UtilsService,
79 private itembuffer: ItemBufferService, 93 private itembuffer: ItemBufferService,
80 private dialog: MatDialog) { 94 private dialog: MatDialog) {
@@ -342,6 +356,17 @@ export class ImportExportService { @@ -342,6 +356,17 @@ export class ImportExportService {
342 ); 356 );
343 } 357 }
344 358
  359 + public bulkImportEntities(entitiesData: BulkImportRequest, entityType: EntityType, config?: RequestConfig): Observable<BulkImportResult> {
  360 + switch (entityType) {
  361 + case EntityType.DEVICE:
  362 + return this.deviceService.bulkImportDevices(entitiesData, config);
  363 + case EntityType.ASSET:
  364 + return this.assetService.bulkImportAssets(entitiesData, config);
  365 + case EntityType.EDGE:
  366 + return this.edgeService.bulkImportEdges(entitiesData, config);
  367 + }
  368 + }
  369 +
345 public importEntities(entitiesData: ImportEntityData[], entityType: EntityType, updateData: boolean, 370 public importEntities(entitiesData: ImportEntityData[], entityType: EntityType, updateData: boolean,
346 importEntityCompleted?: () => void, config?: RequestConfig): Observable<ImportEntitiesResultInfo> { 371 importEntityCompleted?: () => void, config?: RequestConfig): Observable<ImportEntitiesResultInfo> {
347 let partSize = 100; 372 let partSize = 100;