Commit 2696c1b8ec4cbfd095eae050bc330b2b5c9d690c
1 parent
41e57249
UI: Implementation of new endpoint for bulk import
Showing
7 changed files
with
101 additions
and
205 deletions
@@ -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; |