Commit 41e572490ef373f6d2718f1fe8e8f10b7e986b88
Merge branch 'feature/bulk-import/device-credentials' of https://github.com/vvll…
…add28/thingsboard into feature/bulk-import
Showing
12 changed files
with
395 additions
and
48 deletions
@@ -53,7 +53,7 @@ import { | @@ -53,7 +53,7 @@ import { | ||
53 | ImportEntityData | 53 | ImportEntityData |
54 | } from '@shared/models/entity.models'; | 54 | } from '@shared/models/entity.models'; |
55 | import { EntityRelationService } from '@core/http/entity-relation.service'; | 55 | import { EntityRelationService } from '@core/http/entity-relation.service'; |
56 | -import { deepClone, generateSecret, guid, isDefined, isDefinedAndNotNull } from '@core/utils'; | 56 | +import { deepClone, generateSecret, guid, isDefined, isDefinedAndNotNull, isNotEmptyStr } from '@core/utils'; |
57 | import { Asset } from '@shared/models/asset.models'; | 57 | import { Asset } from '@shared/models/asset.models'; |
58 | import { Device, DeviceCredentialsType } from '@shared/models/device.models'; | 58 | import { Device, DeviceCredentialsType } from '@shared/models/device.models'; |
59 | import { AttributeService } from '@core/http/attribute.service'; | 59 | import { AttributeService } from '@core/http/attribute.service'; |
@@ -954,7 +954,12 @@ export class EntityService { | @@ -954,7 +954,12 @@ export class EntityService { | ||
954 | map(() => { | 954 | map(() => { |
955 | return { create: { entity: 1 } } as ImportEntitiesResultInfo; | 955 | return { create: { entity: 1 } } as ImportEntitiesResultInfo; |
956 | }), | 956 | }), |
957 | - catchError(err => of({ error: { entity: 1 } } as ImportEntitiesResultInfo)) | 957 | + catchError(err => of({ |
958 | + error: { | ||
959 | + entity: 1, | ||
960 | + errors: err.message | ||
961 | + } | ||
962 | + } as ImportEntitiesResultInfo)) | ||
958 | ); | 963 | ); |
959 | }), | 964 | }), |
960 | catchError(err => { | 965 | catchError(err => { |
@@ -978,13 +983,28 @@ export class EntityService { | @@ -978,13 +983,28 @@ export class EntityService { | ||
978 | map(() => { | 983 | map(() => { |
979 | return { update: { entity: 1 } } as ImportEntitiesResultInfo; | 984 | return { update: { entity: 1 } } as ImportEntitiesResultInfo; |
980 | }), | 985 | }), |
981 | - catchError(updateError => of({ error: { entity: 1 } } as ImportEntitiesResultInfo)) | 986 | + catchError(updateError => of({ |
987 | + error: { | ||
988 | + entity: 1, | ||
989 | + errors: updateError.message | ||
990 | + } | ||
991 | + } as ImportEntitiesResultInfo)) | ||
982 | ); | 992 | ); |
983 | }), | 993 | }), |
984 | - catchError(findErr => of({ error: { entity: 1 } } as ImportEntitiesResultInfo)) | 994 | + catchError(findErr => of({ |
995 | + error: { | ||
996 | + entity: 1, | ||
997 | + errors: `Line: ${entityData.lineNumber}; Error: ${findErr.error.message}` | ||
998 | + } | ||
999 | + } as ImportEntitiesResultInfo)) | ||
985 | ); | 1000 | ); |
986 | } else { | 1001 | } else { |
987 | - return of({ error: { entity: 1 } } as ImportEntitiesResultInfo); | 1002 | + return of({ |
1003 | + error: { | ||
1004 | + entity: 1, | ||
1005 | + errors: `Line: ${entityData.lineNumber}; Error: ${err.error.message}` | ||
1006 | + } | ||
1007 | + } as ImportEntitiesResultInfo); | ||
988 | } | 1008 | } |
989 | }) | 1009 | }) |
990 | ); | 1010 | ); |
@@ -1040,7 +1060,6 @@ export class EntityService { | @@ -1040,7 +1060,6 @@ export class EntityService { | ||
1040 | break; | 1060 | break; |
1041 | } | 1061 | } |
1042 | return saveEntityObservable; | 1062 | return saveEntityObservable; |
1043 | - | ||
1044 | } | 1063 | } |
1045 | 1064 | ||
1046 | private getUpdateEntityTasks(entityType: EntityType, entityData: ImportEntityData | EdgeImportEntityData, | 1065 | private getUpdateEntityTasks(entityType: EntityType, entityData: ImportEntityData | EdgeImportEntityData, |
@@ -1113,15 +1132,31 @@ export class EntityService { | @@ -1113,15 +1132,31 @@ export class EntityService { | ||
1113 | public saveEntityData(entityId: EntityId, entityData: ImportEntityData, config?: RequestConfig): Observable<any> { | 1132 | public saveEntityData(entityId: EntityId, entityData: ImportEntityData, config?: RequestConfig): Observable<any> { |
1114 | const observables: Observable<string>[] = []; | 1133 | const observables: Observable<string>[] = []; |
1115 | let observable: Observable<string>; | 1134 | let observable: Observable<string>; |
1116 | - if (entityData.accessToken && entityData.accessToken !== '') { | 1135 | + if (Object.keys(entityData.credential).length) { |
1136 | + let credentialsType: DeviceCredentialsType; | ||
1137 | + let credentialsId: string = null; | ||
1138 | + let credentialsValue: string = null; | ||
1139 | + if (isDefinedAndNotNull(entityData.credential.mqtt)) { | ||
1140 | + credentialsType = DeviceCredentialsType.MQTT_BASIC; | ||
1141 | + credentialsValue = JSON.stringify(entityData.credential.mqtt); | ||
1142 | + } else if (isDefinedAndNotNull(entityData.credential.lwm2m)) { | ||
1143 | + credentialsType = DeviceCredentialsType.LWM2M_CREDENTIALS; | ||
1144 | + credentialsValue = JSON.stringify(entityData.credential.lwm2m); | ||
1145 | + } else if (isNotEmptyStr(entityData.credential.x509)) { | ||
1146 | + credentialsType = DeviceCredentialsType.X509_CERTIFICATE; | ||
1147 | + credentialsValue = entityData.credential.x509; | ||
1148 | + } else { | ||
1149 | + credentialsType = DeviceCredentialsType.ACCESS_TOKEN; | ||
1150 | + credentialsId = entityData.credential.accessToken; | ||
1151 | + } | ||
1117 | observable = this.deviceService.getDeviceCredentials(entityId.id, false, config).pipe( | 1152 | observable = this.deviceService.getDeviceCredentials(entityId.id, false, config).pipe( |
1118 | mergeMap((credentials) => { | 1153 | mergeMap((credentials) => { |
1119 | - credentials.credentialsId = entityData.accessToken; | ||
1120 | - credentials.credentialsType = DeviceCredentialsType.ACCESS_TOKEN; | ||
1121 | - credentials.credentialsValue = null; | 1154 | + credentials.credentialsId = credentialsId; |
1155 | + credentials.credentialsType = credentialsType; | ||
1156 | + credentials.credentialsValue = credentialsValue; | ||
1122 | return this.deviceService.saveDeviceCredentials(credentials, config).pipe( | 1157 | return this.deviceService.saveDeviceCredentials(credentials, config).pipe( |
1123 | map(() => 'ok'), | 1158 | map(() => 'ok'), |
1124 | - catchError(err => of('error')) | 1159 | + catchError(err => of(`Line: ${entityData.lineNumber}; Error: ${err.error.message}`)) |
1125 | ); | 1160 | ); |
1126 | }) | 1161 | }) |
1127 | ); | 1162 | ); |
@@ -1131,7 +1166,7 @@ export class EntityService { | @@ -1131,7 +1166,7 @@ export class EntityService { | ||
1131 | observable = this.attributeService.saveEntityAttributes(entityId, AttributeScope.SHARED_SCOPE, | 1166 | observable = this.attributeService.saveEntityAttributes(entityId, AttributeScope.SHARED_SCOPE, |
1132 | entityData.attributes.shared, config).pipe( | 1167 | entityData.attributes.shared, config).pipe( |
1133 | map(() => 'ok'), | 1168 | map(() => 'ok'), |
1134 | - catchError(err => of('error')) | 1169 | + catchError(err => of(`Line: ${entityData.lineNumber}; Error: ${err.error.message}`)) |
1135 | ); | 1170 | ); |
1136 | observables.push(observable); | 1171 | observables.push(observable); |
1137 | } | 1172 | } |
@@ -1139,23 +1174,23 @@ export class EntityService { | @@ -1139,23 +1174,23 @@ export class EntityService { | ||
1139 | observable = this.attributeService.saveEntityAttributes(entityId, AttributeScope.SERVER_SCOPE, | 1174 | observable = this.attributeService.saveEntityAttributes(entityId, AttributeScope.SERVER_SCOPE, |
1140 | entityData.attributes.server, config).pipe( | 1175 | entityData.attributes.server, config).pipe( |
1141 | map(() => 'ok'), | 1176 | map(() => 'ok'), |
1142 | - catchError(err => of('error')) | 1177 | + catchError(err => of(`Line: ${entityData.lineNumber}; Error: ${err.error.message}`)) |
1143 | ); | 1178 | ); |
1144 | observables.push(observable); | 1179 | observables.push(observable); |
1145 | } | 1180 | } |
1146 | if (entityData.timeseries && entityData.timeseries.length) { | 1181 | if (entityData.timeseries && entityData.timeseries.length) { |
1147 | observable = this.attributeService.saveEntityTimeseries(entityId, 'time', entityData.timeseries, config).pipe( | 1182 | observable = this.attributeService.saveEntityTimeseries(entityId, 'time', entityData.timeseries, config).pipe( |
1148 | map(() => 'ok'), | 1183 | map(() => 'ok'), |
1149 | - catchError(err => of('error')) | 1184 | + catchError(err => of(`Line: ${entityData.lineNumber}; Error: ${err.error.message}`)) |
1150 | ); | 1185 | ); |
1151 | observables.push(observable); | 1186 | observables.push(observable); |
1152 | } | 1187 | } |
1153 | if (observables.length) { | 1188 | if (observables.length) { |
1154 | return forkJoin(observables).pipe( | 1189 | return forkJoin(observables).pipe( |
1155 | map((response) => { | 1190 | map((response) => { |
1156 | - const hasError = response.filter((status) => status === 'error').length > 0; | ||
1157 | - if (hasError) { | ||
1158 | - throw Error(); | 1191 | + const hasError = response.filter((status) => status !== 'ok'); |
1192 | + if (hasError.length > 0) { | ||
1193 | + throw Error(hasError.join('\n')); | ||
1159 | } else { | 1194 | } else { |
1160 | return response; | 1195 | return response; |
1161 | } | 1196 | } |
@@ -94,7 +94,7 @@ | @@ -94,7 +94,7 @@ | ||
94 | <mat-step [stepControl]="columnTypesFormGroup"> | 94 | <mat-step [stepControl]="columnTypesFormGroup"> |
95 | <form [formGroup]="columnTypesFormGroup"> | 95 | <form [formGroup]="columnTypesFormGroup"> |
96 | <ng-template matStepLabel>{{ 'import.stepper-text.column-type' | translate }}</ng-template> | 96 | <ng-template matStepLabel>{{ 'import.stepper-text.column-type' | translate }}</ng-template> |
97 | - <tb-table-columns-assignment formControlName="columnsParam" [entityType]="entityType"></tb-table-columns-assignment> | 97 | + <tb-table-columns-assignment #columnsAssignmentComponent formControlName="columnsParam" [entityType]="entityType"></tb-table-columns-assignment> |
98 | </form> | 98 | </form> |
99 | <div fxLayout="row wrap" fxLayoutAlign="space-between center"> | 99 | <div fxLayout="row wrap" fxLayoutAlign="space-between center"> |
100 | <button mat-button | 100 | <button mat-button |
@@ -125,9 +125,20 @@ | @@ -125,9 +125,20 @@ | ||
125 | <p class="mat-body-1" *ngIf="this.statistical?.update && this.statistical?.update.entity"> | 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}) }} | 126 | {{ translate.instant('import.message.update-entities', {count: this.statistical.update.entity}) }} |
127 | </p> | 127 | </p> |
128 | - <p class="mat-body-1" *ngIf="this.statistical?.error && this.statistical?.error.entity"> | 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}) }} | 129 | {{ translate.instant('import.message.error-entities', {count: this.statistical.error.entity}) }} |
130 | </p> | 130 | </p> |
131 | + <mat-expansion-panel class="advanced-logs" [expanded]="false" | ||
132 | + *ngIf="this.statistical?.error && this.statistical?.error.entity" | ||
133 | + (opened)="initEditor()"> | ||
134 | + <mat-expansion-panel-header [collapsedHeight]="'38px'" [expandedHeight]="'38px'"> | ||
135 | + <mat-panel-title> | ||
136 | + <div class="tb-small" translate>import.details</div> | ||
137 | + </mat-panel-title> | ||
138 | + </mat-expansion-panel-header> | ||
139 | + <mat-divider></mat-divider> | ||
140 | + <div #failureDetailsEditor class="tb-failure-details"></div> | ||
141 | + </mat-expansion-panel> | ||
131 | </div> | 142 | </div> |
132 | <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="20px"> | 143 | <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="20px"> |
133 | <button mat-raised-button | 144 | <button mat-raised-button |
@@ -26,5 +26,30 @@ | @@ -26,5 +26,30 @@ | ||
26 | .tb-import-progress{ | 26 | .tb-import-progress{ |
27 | margin: 7px 0; | 27 | margin: 7px 0; |
28 | } | 28 | } |
29 | + | ||
30 | + .tb-failure-details { | ||
31 | + width: 100%; | ||
32 | + min-width: 300px; | ||
33 | + height: 100%; | ||
34 | + min-height: 50px; | ||
35 | + margin-top: 8px; | ||
36 | + } | ||
37 | + | ||
38 | + .mat-expansion-panel { | ||
39 | + box-shadow: none; | ||
40 | + &.advanced-logs { | ||
41 | + border: 1px groove rgba(0, 0, 0, .25); | ||
42 | + padding: 0; | ||
43 | + margin-bottom: 1.6em; | ||
44 | + | ||
45 | + .mat-expansion-panel-header { | ||
46 | + padding: 0 8px; | ||
47 | + } | ||
48 | + | ||
49 | + .mat-expansion-panel-body { | ||
50 | + padding: 0; | ||
51 | + } | ||
52 | + } | ||
53 | + } | ||
29 | } | 54 | } |
30 | } | 55 | } |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Component, Inject, OnInit, ViewChild } from '@angular/core'; | 17 | +import { AfterViewInit, Component, ElementRef, Inject, Renderer2, ViewChild } from '@angular/core'; |
18 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | 18 | import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
19 | import { Store } from '@ngrx/store'; | 19 | import { Store } from '@ngrx/store'; |
20 | import { AppState } from '@core/core.state'; | 20 | import { AppState } from '@core/core.state'; |
@@ -34,6 +34,12 @@ import { | @@ -34,6 +34,12 @@ import { | ||
34 | } from '@home/components/import-export/import-export.models'; | 34 | } from '@home/components/import-export/import-export.models'; |
35 | import { EdgeImportEntityData, ImportEntitiesResultInfo, ImportEntityData } from '@app/shared/models/entity.models'; | 35 | import { EdgeImportEntityData, ImportEntitiesResultInfo, ImportEntityData } from '@app/shared/models/entity.models'; |
36 | import { ImportExportService } from '@home/components/import-export/import-export.service'; | 36 | import { ImportExportService } from '@home/components/import-export/import-export.service'; |
37 | +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'; | ||
42 | +import { getAce } from '@shared/models/ace/ace.models'; | ||
37 | 43 | ||
38 | export interface ImportDialogCsvData { | 44 | export interface ImportDialogCsvData { |
39 | entityType: EntityType; | 45 | entityType: EntityType; |
@@ -48,15 +54,21 @@ export interface ImportDialogCsvData { | @@ -48,15 +54,21 @@ export interface ImportDialogCsvData { | ||
48 | styleUrls: ['./import-dialog-csv.component.scss'] | 54 | styleUrls: ['./import-dialog-csv.component.scss'] |
49 | }) | 55 | }) |
50 | export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvComponent, boolean> | 56 | export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvComponent, boolean> |
51 | - implements OnInit { | 57 | + implements AfterViewInit { |
52 | 58 | ||
53 | @ViewChild('importStepper', {static: true}) importStepper: MatVerticalStepper; | 59 | @ViewChild('importStepper', {static: true}) importStepper: MatVerticalStepper; |
54 | 60 | ||
61 | + @ViewChild('columnsAssignmentComponent', {static: true}) | ||
62 | + columnsAssignmentComponent: TableColumnsAssignmentComponent; | ||
63 | + | ||
64 | + @ViewChild('failureDetailsEditor') | ||
65 | + failureDetailsEditorElmRef: ElementRef; | ||
66 | + | ||
55 | entityType: EntityType; | 67 | entityType: EntityType; |
56 | importTitle: string; | 68 | importTitle: string; |
57 | importFileLabel: string; | 69 | importFileLabel: string; |
58 | 70 | ||
59 | - delimiters: {key: string, value: string}[] = [{ | 71 | + delimiters: { key: string, value: string }[] = [{ |
60 | key: ',', | 72 | key: ',', |
61 | value: ',' | 73 | value: ',' |
62 | }, { | 74 | }, { |
@@ -80,6 +92,8 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -80,6 +92,8 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
80 | progressCreate = 0; | 92 | progressCreate = 0; |
81 | statistical: ImportEntitiesResultInfo; | 93 | statistical: ImportEntitiesResultInfo; |
82 | 94 | ||
95 | + private allowAssignColumn: ImportEntityColumnType[]; | ||
96 | + private initEditorComponent = false; | ||
83 | private parseData: CsvToJsonResult; | 97 | private parseData: CsvToJsonResult; |
84 | 98 | ||
85 | constructor(protected store: Store<AppState>, | 99 | constructor(protected store: Store<AppState>, |
@@ -88,7 +102,8 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -88,7 +102,8 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
88 | public dialogRef: MatDialogRef<ImportDialogCsvComponent, boolean>, | 102 | public dialogRef: MatDialogRef<ImportDialogCsvComponent, boolean>, |
89 | public translate: TranslateService, | 103 | public translate: TranslateService, |
90 | private importExport: ImportExportService, | 104 | private importExport: ImportExportService, |
91 | - private fb: FormBuilder) { | 105 | + private fb: FormBuilder, |
106 | + private renderer: Renderer2) { | ||
92 | super(store, router, dialogRef); | 107 | super(store, router, dialogRef); |
93 | this.entityType = data.entityType; | 108 | this.entityType = data.entityType; |
94 | this.importTitle = data.importTitle; | 109 | this.importTitle = data.importTitle; |
@@ -109,7 +124,12 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -109,7 +124,12 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
109 | }); | 124 | }); |
110 | } | 125 | } |
111 | 126 | ||
112 | - ngOnInit(): void { | 127 | + ngAfterViewInit() { |
128 | + let columns = this.columnsAssignmentComponent.columnTypes; | ||
129 | + if (this.entityType === EntityType.DEVICE) { | ||
130 | + columns = columns.concat(this.columnsAssignmentComponent.columnDeviceCredentials); | ||
131 | + } | ||
132 | + this.allowAssignColumn = columns.map(column => column.value); | ||
113 | } | 133 | } |
114 | 134 | ||
115 | cancel(): void { | 135 | cancel(): void { |
@@ -157,8 +177,10 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -157,8 +177,10 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
157 | return convertCSVToJson(importData, config, | 177 | return convertCSVToJson(importData, config, |
158 | (messageId, params) => { | 178 | (messageId, params) => { |
159 | this.store.dispatch(new ActionNotificationShow( | 179 | this.store.dispatch(new ActionNotificationShow( |
160 | - {message: this.translate.instant(messageId, params), | ||
161 | - type: 'error'})); | 180 | + { |
181 | + message: this.translate.instant(messageId, params), | ||
182 | + type: 'error' | ||
183 | + })); | ||
162 | } | 184 | } |
163 | ); | 185 | ); |
164 | } | 186 | } |
@@ -168,9 +190,14 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -168,9 +190,14 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
168 | const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value; | 190 | const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value; |
169 | for (let i = 0; i < this.parseData.headers.length; i++) { | 191 | for (let i = 0; i < this.parseData.headers.length; i++) { |
170 | let columnParam: CsvColumnParam; | 192 | let columnParam: CsvColumnParam; |
171 | - if (isHeader && this.parseData.headers[i].search(/^(name|type|label)$/im) === 0) { | 193 | + let findEntityColumnType: ImportEntityColumnType; |
194 | + if (isHeader) { | ||
195 | + const headerColumnName = this.parseData.headers[i].toUpperCase(); | ||
196 | + findEntityColumnType = this.allowAssignColumn.find(column => column === headerColumnName); | ||
197 | + } | ||
198 | + if (isHeader && findEntityColumnType) { | ||
172 | columnParam = { | 199 | columnParam = { |
173 | - type: ImportEntityColumnType[this.parseData.headers[i].toLowerCase()], | 200 | + type: findEntityColumnType, |
174 | key: this.parseData.headers[i].toLowerCase(), | 201 | key: this.parseData.headers[i].toLowerCase(), |
175 | sampleData: this.parseData.rows[0][i] | 202 | sampleData: this.parseData.rows[0][i] |
176 | }; | 203 | }; |
@@ -189,13 +216,19 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -189,13 +216,19 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
189 | 216 | ||
190 | private addEntities() { | 217 | private addEntities() { |
191 | const importData = this.parseData; | 218 | const importData = this.parseData; |
219 | + const isHeader: boolean = this.importParametersFormGroup.get('isHeader').value; | ||
192 | const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value; | 220 | const parameterColumns: CsvColumnParam[] = this.columnTypesFormGroup.get('columnsParam').value; |
193 | const entitiesData: ImportEntityData[] = []; | 221 | const entitiesData: ImportEntityData[] = []; |
194 | let sentDataLength = 0; | 222 | let sentDataLength = 0; |
223 | + const startLineNumber = isHeader ? 2 : 1; | ||
195 | for (let row = 0; row < importData.rows.length; row++) { | 224 | for (let row = 0; row < importData.rows.length; row++) { |
196 | const entityData: ImportEntityData = this.constructDraftImportEntityData(); | 225 | const entityData: ImportEntityData = this.constructDraftImportEntityData(); |
197 | const i = row; | 226 | const i = row; |
227 | + entityData.lineNumber = startLineNumber + i; | ||
198 | for (let j = 0; j < parameterColumns.length; j++) { | 228 | for (let j = 0; j < parameterColumns.length; j++) { |
229 | + if (!isDefinedAndNotNull(importData.rows[i][j]) || importData.rows[i][j] === '') { | ||
230 | + continue; | ||
231 | + } | ||
199 | switch (parameterColumns[j].type) { | 232 | switch (parameterColumns[j].type) { |
200 | case ImportEntityColumnType.serverAttribute: | 233 | case ImportEntityColumnType.serverAttribute: |
201 | entityData.attributes.server.push({ | 234 | entityData.attributes.server.push({ |
@@ -215,9 +248,6 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -215,9 +248,6 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
215 | value: importData.rows[i][j] | 248 | value: importData.rows[i][j] |
216 | }); | 249 | }); |
217 | break; | 250 | break; |
218 | - case ImportEntityColumnType.accessToken: | ||
219 | - entityData.accessToken = importData.rows[i][j]; | ||
220 | - break; | ||
221 | case ImportEntityColumnType.name: | 251 | case ImportEntityColumnType.name: |
222 | entityData.name = importData.rows[i][j]; | 252 | entityData.name = importData.rows[i][j]; |
223 | break; | 253 | break; |
@@ -233,6 +263,96 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -233,6 +263,96 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
233 | case ImportEntityColumnType.description: | 263 | case ImportEntityColumnType.description: |
234 | entityData.description = importData.rows[i][j]; | 264 | entityData.description = importData.rows[i][j]; |
235 | break; | 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; | ||
236 | case ImportEntityColumnType.edgeLicenseKey: | 356 | case ImportEntityColumnType.edgeLicenseKey: |
237 | (entityData as EdgeImportEntityData).edgeLicenseKey = importData.rows[i][j]; | 357 | (entityData as EdgeImportEntityData).edgeLicenseKey = importData.rows[i][j]; |
238 | break; | 358 | break; |
@@ -268,16 +388,17 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -268,16 +388,17 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
268 | 388 | ||
269 | private constructDraftImportEntityData(): ImportEntityData { | 389 | private constructDraftImportEntityData(): ImportEntityData { |
270 | const entityData: ImportEntityData = { | 390 | const entityData: ImportEntityData = { |
391 | + lineNumber: 1, | ||
271 | name: '', | 392 | name: '', |
272 | type: '', | 393 | type: '', |
273 | description: '', | 394 | description: '', |
274 | gateway: null, | 395 | gateway: null, |
275 | label: '', | 396 | label: '', |
276 | - accessToken: '', | ||
277 | attributes: { | 397 | attributes: { |
278 | server: [], | 398 | server: [], |
279 | shared: [] | 399 | shared: [] |
280 | }, | 400 | }, |
401 | + credential: {}, | ||
281 | timeseries: [] | 402 | timeseries: [] |
282 | }; | 403 | }; |
283 | if (this.entityType === EntityType.EDGE) { | 404 | if (this.entityType === EntityType.EDGE) { |
@@ -292,5 +413,49 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | @@ -292,5 +413,49 @@ export class ImportDialogCsvComponent extends DialogComponent<ImportDialogCsvCom | ||
292 | } | 413 | } |
293 | } | 414 | } |
294 | 415 | ||
416 | + initEditor() { | ||
417 | + if (!this.initEditorComponent) { | ||
418 | + this.createEditor(this.failureDetailsEditorElmRef, this.statistical.error.errors); | ||
419 | + } | ||
420 | + } | ||
421 | + | ||
422 | + private createEditor(editorElementRef: ElementRef, content: string): void { | ||
423 | + const editorElement = editorElementRef.nativeElement; | ||
424 | + let editorOptions: Partial<Ace.EditorOptions> = { | ||
425 | + mode: 'ace/mode/java', | ||
426 | + theme: 'ace/theme/github', | ||
427 | + showGutter: false, | ||
428 | + showPrintMargin: false, | ||
429 | + readOnly: true | ||
430 | + }; | ||
431 | + | ||
432 | + const advancedOptions = { | ||
433 | + enableSnippets: false, | ||
434 | + enableBasicAutocompletion: false, | ||
435 | + enableLiveAutocompletion: false | ||
436 | + }; | ||
437 | + | ||
438 | + editorOptions = {...editorOptions, ...advancedOptions}; | ||
439 | + getAce().subscribe( | ||
440 | + (ace) => { | ||
441 | + const editor = ace.edit(editorElement, editorOptions); | ||
442 | + editor.session.setUseWrapMode(false); | ||
443 | + editor.setValue(content, -1); | ||
444 | + this.updateEditorSize(editorElement, content, editor); | ||
445 | + } | ||
446 | + ); | ||
447 | + } | ||
448 | + | ||
449 | + private updateEditorSize(editorElement: any, content: string, editor: Ace.Editor) { | ||
450 | + let newHeight = 200; | ||
451 | + if (content && content.length > 0) { | ||
452 | + const lines = content.split('\n'); | ||
453 | + newHeight = 16 * lines.length + 24; | ||
454 | + } | ||
455 | + const minHeight = Math.min(200, newHeight); | ||
456 | + this.renderer.setStyle(editorElement, 'minHeight', minHeight.toString() + 'px'); | ||
457 | + this.renderer.setStyle(editorElement, 'height', newHeight.toString() + 'px'); | ||
458 | + editor.resize(); | ||
459 | + } | ||
295 | 460 | ||
296 | } | 461 | } |
@@ -14,7 +14,7 @@ | @@ -14,7 +14,7 @@ | ||
14 | /// limitations under the License. | 14 | /// limitations under the License. |
15 | /// | 15 | /// |
16 | 16 | ||
17 | -import { Widget, WidgetType, WidgetTypeDetails } from '@app/shared/models/widget.models'; | 17 | +import { Widget, WidgetTypeDetails } from '@app/shared/models/widget.models'; |
18 | import { DashboardLayoutId } from '@shared/models/dashboard.models'; | 18 | import { DashboardLayoutId } from '@shared/models/dashboard.models'; |
19 | import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; | 19 | import { WidgetsBundle } from '@shared/models/widgets-bundle.model'; |
20 | 20 | ||
@@ -46,8 +46,22 @@ export enum ImportEntityColumnType { | @@ -46,8 +46,22 @@ export enum ImportEntityColumnType { | ||
46 | sharedAttribute = 'SHARED_ATTRIBUTE', | 46 | sharedAttribute = 'SHARED_ATTRIBUTE', |
47 | serverAttribute = 'SERVER_ATTRIBUTE', | 47 | serverAttribute = 'SERVER_ATTRIBUTE', |
48 | timeseries = 'TIMESERIES', | 48 | timeseries = 'TIMESERIES', |
49 | - entityField = 'ENTITY_FIELD', | ||
50 | accessToken = 'ACCESS_TOKEN', | 49 | accessToken = 'ACCESS_TOKEN', |
50 | + x509 = 'X509', | ||
51 | + mqttClientId = 'MQTT_CLIENT_ID', | ||
52 | + mqttUserName = 'MQTT_USER_NAME', | ||
53 | + mqttPassword = 'MQTT_PASSWORD', | ||
54 | + lwm2mClientEndpoint = 'LWM2M_CLIENT_ENDPOINT', | ||
55 | + lwm2mClientSecurityConfigMode = 'LWM2M_CLIENT_SECURITY_CONFIG_MODE', | ||
56 | + lwm2mClientIdentity = 'LWM2M_CLIENT_IDENTITY', | ||
57 | + lwm2mClientKey = 'LWM2M_CLIENT_KEY', | ||
58 | + lwm2mClientCert = 'LWM2M_CLIENT_CERT', | ||
59 | + lwm2mBootstrapServerSecurityMode = 'LWM2M_BOOTSTRAP_SERVER_SECURITY_MODE', | ||
60 | + lwm2mBootstrapServerClientPublicKeyOrId = 'LWM2M_BOOTSTRAP_SERVER_PUBLIC_KEY_OR_ID', | ||
61 | + lwm2mBootstrapServerClientSecretKey = 'LWM2M_BOOTSTRAP_SERVER_SECRET_KEY', | ||
62 | + lwm2mServerSecurityMode = 'LWM2M_SERVER_SECURITY_MODE', | ||
63 | + lwm2mServerClientPublicKeyOrId = 'LWM2M_SERVER_CLIENT_PUBLIC_KEY_OR_ID', | ||
64 | + lwm2mServerClientSecretKey = 'LWM2M_SERVER_CLIENT_SECRET_KEY', | ||
51 | isGateway = 'IS_GATEWAY', | 65 | isGateway = 'IS_GATEWAY', |
52 | description = 'DESCRIPTION', | 66 | description = 'DESCRIPTION', |
53 | edgeLicenseKey = 'EDGE_LICENSE_KEY', | 67 | edgeLicenseKey = 'EDGE_LICENSE_KEY', |
@@ -68,8 +82,22 @@ export const importEntityColumnTypeTranslations = new Map<ImportEntityColumnType | @@ -68,8 +82,22 @@ export const importEntityColumnTypeTranslations = new Map<ImportEntityColumnType | ||
68 | [ImportEntityColumnType.sharedAttribute, 'import.column-type.shared-attribute'], | 82 | [ImportEntityColumnType.sharedAttribute, 'import.column-type.shared-attribute'], |
69 | [ImportEntityColumnType.serverAttribute, 'import.column-type.server-attribute'], | 83 | [ImportEntityColumnType.serverAttribute, 'import.column-type.server-attribute'], |
70 | [ImportEntityColumnType.timeseries, 'import.column-type.timeseries'], | 84 | [ImportEntityColumnType.timeseries, 'import.column-type.timeseries'], |
71 | - [ImportEntityColumnType.entityField, 'import.column-type.entity-field'], | ||
72 | [ImportEntityColumnType.accessToken, 'import.column-type.access-token'], | 85 | [ImportEntityColumnType.accessToken, 'import.column-type.access-token'], |
86 | + [ImportEntityColumnType.x509, 'import.column-type.x509'], | ||
87 | + [ImportEntityColumnType.mqttClientId, 'import.column-type.mqtt.client-id'], | ||
88 | + [ImportEntityColumnType.mqttUserName, 'import.column-type.mqtt.user-name'], | ||
89 | + [ImportEntityColumnType.mqttPassword, 'import.column-type.mqtt.password'], | ||
90 | + [ImportEntityColumnType.lwm2mClientEndpoint, 'import.column-type.lwm2m.client-endpoint'], | ||
91 | + [ImportEntityColumnType.lwm2mClientSecurityConfigMode, 'import.column-type.lwm2m.security-config-mode'], | ||
92 | + [ImportEntityColumnType.lwm2mClientIdentity, 'import.column-type.lwm2m.client-identity'], | ||
93 | + [ImportEntityColumnType.lwm2mClientKey, 'import.column-type.lwm2m.client-key'], | ||
94 | + [ImportEntityColumnType.lwm2mClientCert, 'import.column-type.lwm2m.client-cert'], | ||
95 | + [ImportEntityColumnType.lwm2mBootstrapServerSecurityMode, 'import.column-type.lwm2m.bootstrap-server-security-mode'], | ||
96 | + [ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId, 'import.column-type.lwm2m.bootstrap-server-public-key-id'], | ||
97 | + [ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey, 'import.column-type.lwm2m.bootstrap-server-secret-key'], | ||
98 | + [ImportEntityColumnType.lwm2mServerSecurityMode, 'import.column-type.lwm2m.lwm2m-server-security-mode'], | ||
99 | + [ImportEntityColumnType.lwm2mServerClientPublicKeyOrId, 'import.column-type.lwm2m.lwm2m-server-public-key-id'], | ||
100 | + [ImportEntityColumnType.lwm2mServerClientSecretKey, 'import.column-type.lwm2m.lwm2m-server-secret-key'], | ||
73 | [ImportEntityColumnType.isGateway, 'import.column-type.isgateway'], | 101 | [ImportEntityColumnType.isGateway, 'import.column-type.isgateway'], |
74 | [ImportEntityColumnType.description, 'import.column-type.description'], | 102 | [ImportEntityColumnType.description, 'import.column-type.description'], |
75 | [ImportEntityColumnType.edgeLicenseKey, 'import.column-type.edge-license-key'], | 103 | [ImportEntityColumnType.edgeLicenseKey, 'import.column-type.edge-license-key'], |
@@ -21,7 +21,7 @@ import { Store } from '@ngrx/store'; | @@ -21,7 +21,7 @@ import { Store } from '@ngrx/store'; | ||
21 | import { AppState } from '@core/core.state'; | 21 | import { AppState } from '@core/core.state'; |
22 | import { ActionNotificationShow } from '@core/notification/notification.actions'; | 22 | import { ActionNotificationShow } from '@core/notification/notification.actions'; |
23 | import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; | 23 | import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models'; |
24 | -import { deepClone, isDefined, isObject, isUndefined } from '@core/utils'; | 24 | +import { deepClone, isDefined, isObject, isString, isUndefined } from '@core/utils'; |
25 | import { WINDOW } from '@core/services/window.service'; | 25 | import { WINDOW } from '@core/services/window.service'; |
26 | import { DOCUMENT } from '@angular/common'; | 26 | import { DOCUMENT } from '@angular/common'; |
27 | import { | 27 | import { |
@@ -563,6 +563,8 @@ export class ImportExportService { | @@ -563,6 +563,8 @@ export class ImportExportService { | ||
563 | if (isObject(obj2[key])) { | 563 | if (isObject(obj2[key])) { |
564 | obj1[key] = obj1[key] || {}; | 564 | obj1[key] = obj1[key] || {}; |
565 | obj1[key] = {...obj1[key], ...this.sumObject(obj1[key], obj2[key])}; | 565 | obj1[key] = {...obj1[key], ...this.sumObject(obj1[key], obj2[key])}; |
566 | + } else if (isString(obj2[key])) { | ||
567 | + obj1[key] = (obj1[key] || '') + `${obj2[key]}\n`; | ||
566 | } else { | 568 | } else { |
567 | obj1[key] = (obj1[key] || 0) + obj2[key]; | 569 | obj1[key] = (obj1[key] || 0) + obj2[key]; |
568 | } | 570 | } |
@@ -31,10 +31,15 @@ | @@ -31,10 +31,15 @@ | ||
31 | <ng-container matColumnDef="type"> | 31 | <ng-container matColumnDef="type"> |
32 | <mat-header-cell *matHeaderCellDef style="flex: 0 0 40%" class="mat-column-type"> {{ 'import.column-type.column-type' | translate }} </mat-header-cell> | 32 | <mat-header-cell *matHeaderCellDef style="flex: 0 0 40%" class="mat-column-type"> {{ 'import.column-type.column-type' | translate }} </mat-header-cell> |
33 | <mat-cell *matCellDef="let column"> | 33 | <mat-cell *matCellDef="let column"> |
34 | - <mat-select matInput [(ngModel)]="column.type" (ngModelChange)="columnsUpdated()"> | 34 | + <mat-select [(ngModel)]="column.type" (ngModelChange)="columnsUpdated()"> |
35 | <mat-option *ngFor="let type of columnTypes" [value]="type.value" [disabled]="type.disabled"> | 35 | <mat-option *ngFor="let type of columnTypes" [value]="type.value" [disabled]="type.disabled"> |
36 | {{ columnTypesTranslations.get(type.value) | translate }} | 36 | {{ columnTypesTranslations.get(type.value) | translate }} |
37 | </mat-option> | 37 | </mat-option> |
38 | + <mat-optgroup label="{{ 'import.credentials' | translate }}" *ngIf="entityType === entityTypeDevice"> | ||
39 | + <mat-option *ngFor="let credential of columnDeviceCredentials" [value]="credential.value" [disabled]="credential.disabled"> | ||
40 | + {{ columnTypesTranslations.get(credential.value) | translate }} | ||
41 | + </mat-option> | ||
42 | + </mat-optgroup> | ||
38 | </mat-select> | 43 | </mat-select> |
39 | </mat-cell> | 44 | </mat-cell> |
40 | </ng-container> | 45 | </ng-container> |
@@ -57,8 +57,12 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | @@ -57,8 +57,12 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | ||
57 | 57 | ||
58 | columnTypes: AssignmentColumnType[] = []; | 58 | columnTypes: AssignmentColumnType[] = []; |
59 | 59 | ||
60 | + columnDeviceCredentials: AssignmentColumnType[] = []; | ||
61 | + | ||
60 | columnTypesTranslations = importEntityColumnTypeTranslations; | 62 | columnTypesTranslations = importEntityColumnTypeTranslations; |
61 | 63 | ||
64 | + readonly entityTypeDevice = EntityType.DEVICE; | ||
65 | + | ||
62 | private columns: CsvColumnParam[]; | 66 | private columns: CsvColumnParam[]; |
63 | 67 | ||
64 | private valid = true; | 68 | private valid = true; |
@@ -83,9 +87,26 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | @@ -83,9 +87,26 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | ||
83 | { value: ImportEntityColumnType.sharedAttribute }, | 87 | { value: ImportEntityColumnType.sharedAttribute }, |
84 | { value: ImportEntityColumnType.serverAttribute }, | 88 | { value: ImportEntityColumnType.serverAttribute }, |
85 | { value: ImportEntityColumnType.timeseries }, | 89 | { value: ImportEntityColumnType.timeseries }, |
86 | - { value: ImportEntityColumnType.accessToken }, | ||
87 | { value: ImportEntityColumnType.isGateway } | 90 | { value: ImportEntityColumnType.isGateway } |
88 | ); | 91 | ); |
92 | + this.columnDeviceCredentials.push( | ||
93 | + { value: ImportEntityColumnType.accessToken }, | ||
94 | + { value: ImportEntityColumnType.x509 }, | ||
95 | + { value: ImportEntityColumnType.mqttClientId }, | ||
96 | + { value: ImportEntityColumnType.mqttUserName }, | ||
97 | + { value: ImportEntityColumnType.mqttPassword }, | ||
98 | + { value: ImportEntityColumnType.lwm2mClientEndpoint }, | ||
99 | + { value: ImportEntityColumnType.lwm2mClientSecurityConfigMode }, | ||
100 | + { value: ImportEntityColumnType.lwm2mClientIdentity }, | ||
101 | + { value: ImportEntityColumnType.lwm2mClientKey }, | ||
102 | + { value: ImportEntityColumnType.lwm2mClientCert }, | ||
103 | + { value: ImportEntityColumnType.lwm2mBootstrapServerSecurityMode }, | ||
104 | + { value: ImportEntityColumnType.lwm2mBootstrapServerClientPublicKeyOrId }, | ||
105 | + { value: ImportEntityColumnType.lwm2mBootstrapServerClientSecretKey }, | ||
106 | + { value: ImportEntityColumnType.lwm2mServerSecurityMode }, | ||
107 | + { value: ImportEntityColumnType.lwm2mServerClientPublicKeyOrId }, | ||
108 | + { value: ImportEntityColumnType.lwm2mServerClientSecretKey }, | ||
109 | + ); | ||
89 | break; | 110 | break; |
90 | case EntityType.ASSET: | 111 | case EntityType.ASSET: |
91 | this.columnTypes.push( | 112 | this.columnTypes.push( |
@@ -123,8 +144,6 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | @@ -123,8 +144,6 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | ||
123 | const isSelectName = this.columns.findIndex((column) => column.type === ImportEntityColumnType.name) > -1; | 144 | const isSelectName = this.columns.findIndex((column) => column.type === ImportEntityColumnType.name) > -1; |
124 | const isSelectType = this.columns.findIndex((column) => column.type === ImportEntityColumnType.type) > -1; | 145 | const isSelectType = this.columns.findIndex((column) => column.type === ImportEntityColumnType.type) > -1; |
125 | const isSelectLabel = this.columns.findIndex((column) => column.type === ImportEntityColumnType.label) > -1; | 146 | const isSelectLabel = this.columns.findIndex((column) => column.type === ImportEntityColumnType.label) > -1; |
126 | - const isSelectCredentials = this.columns.findIndex((column) => column.type === ImportEntityColumnType.accessToken) > -1; | ||
127 | - const isSelectGateway = this.columns.findIndex((column) => column.type === ImportEntityColumnType.isGateway) > -1; | ||
128 | const isSelectDescription = this.columns.findIndex((column) => column.type === ImportEntityColumnType.description) > -1; | 147 | const isSelectDescription = this.columns.findIndex((column) => column.type === ImportEntityColumnType.description) > -1; |
129 | const isSelectEdgeLicenseKey = this.columns.findIndex((column) => column.type === ImportEntityColumnType.edgeLicenseKey) > -1; | 148 | const isSelectEdgeLicenseKey = this.columns.findIndex((column) => column.type === ImportEntityColumnType.edgeLicenseKey) > -1; |
130 | const isSelectCloudEndpoint = this.columns.findIndex((column) => column.type === ImportEntityColumnType.cloudEndpoint) > -1; | 149 | const isSelectCloudEndpoint = this.columns.findIndex((column) => column.type === ImportEntityColumnType.cloudEndpoint) > -1; |
@@ -139,14 +158,19 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | @@ -139,14 +158,19 @@ export class TableColumnsAssignmentComponent implements OnInit, ControlValueAcce | ||
139 | this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.label).disabled = isSelectLabel; | 158 | this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.label).disabled = isSelectLabel; |
140 | this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.description).disabled = isSelectDescription; | 159 | this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.description).disabled = isSelectDescription; |
141 | 160 | ||
142 | - const isGatewayColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.isGateway); | ||
143 | - if (isGatewayColumnType) { | ||
144 | - isGatewayColumnType.disabled = isSelectGateway; | ||
145 | - } | ||
146 | - const accessTokenColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.accessToken); | ||
147 | - if (accessTokenColumnType) { | ||
148 | - accessTokenColumnType.disabled = isSelectCredentials; | 161 | + if (this.entityType === EntityType.DEVICE) { |
162 | + const isSelectGateway = this.columns.findIndex((column) => column.type === ImportEntityColumnType.isGateway) > -1; | ||
163 | + | ||
164 | + const isGatewayColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.isGateway); | ||
165 | + if (isGatewayColumnType) { | ||
166 | + isGatewayColumnType.disabled = isSelectGateway; | ||
167 | + } | ||
168 | + | ||
169 | + this.columnDeviceCredentials.forEach((columnCredential) => { | ||
170 | + columnCredential.disabled = this.columns.findIndex(column => column.type === columnCredential.value) > -1; | ||
171 | + }); | ||
149 | } | 172 | } |
173 | + | ||
150 | const edgeLicenseKeyColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.edgeLicenseKey); | 174 | const edgeLicenseKeyColumnType = this.columnTypes.find((columnType) => columnType.value === ImportEntityColumnType.edgeLicenseKey); |
151 | if (edgeLicenseKeyColumnType) { | 175 | if (edgeLicenseKeyColumnType) { |
152 | edgeLicenseKeyColumnType.disabled = isSelectEdgeLicenseKey; | 176 | edgeLicenseKeyColumnType.disabled = isSelectEdgeLicenseKey; |
@@ -743,6 +743,14 @@ export interface DeviceCredentialMQTTBasic { | @@ -743,6 +743,14 @@ export interface DeviceCredentialMQTTBasic { | ||
743 | password: string; | 743 | password: string; |
744 | } | 744 | } |
745 | 745 | ||
746 | +export function getDeviceCredentialMQTTDefault(): DeviceCredentialMQTTBasic { | ||
747 | + return { | ||
748 | + clientId: '', | ||
749 | + userName: '', | ||
750 | + password: '' | ||
751 | + }; | ||
752 | +} | ||
753 | + | ||
746 | export interface DeviceSearchQuery extends EntitySearchQuery { | 754 | export interface DeviceSearchQuery extends EntitySearchQuery { |
747 | deviceTypes: Array<string>; | 755 | deviceTypes: Array<string>; |
748 | } | 756 | } |
@@ -17,6 +17,8 @@ | @@ -17,6 +17,8 @@ | ||
17 | import { EntityType } from '@shared/models/entity-type.models'; | 17 | import { EntityType } from '@shared/models/entity-type.models'; |
18 | import { AttributeData } from './telemetry/telemetry.models'; | 18 | import { AttributeData } from './telemetry/telemetry.models'; |
19 | import { EntityId } from '@shared/models/id/entity-id'; | 19 | import { EntityId } from '@shared/models/id/entity-id'; |
20 | +import { DeviceCredentialMQTTBasic } from '@shared/models/device.models'; | ||
21 | +import { Lwm2mSecurityConfigModels } from '@shared/models/lwm2m-security-config.models'; | ||
20 | 22 | ||
21 | export interface EntityInfo { | 23 | export interface EntityInfo { |
22 | name?: string; | 24 | name?: string; |
@@ -32,12 +34,18 @@ export interface EntityInfoData { | @@ -32,12 +34,18 @@ export interface EntityInfoData { | ||
32 | } | 34 | } |
33 | 35 | ||
34 | export interface ImportEntityData { | 36 | export interface ImportEntityData { |
37 | + lineNumber: number; | ||
35 | name: string; | 38 | name: string; |
36 | type: string; | 39 | type: string; |
37 | label: string; | 40 | label: string; |
38 | gateway: boolean; | 41 | gateway: boolean; |
39 | description: string; | 42 | description: string; |
40 | - accessToken: string; | 43 | + credential: { |
44 | + accessToken?: string; | ||
45 | + x509?: string; | ||
46 | + mqtt?: DeviceCredentialMQTTBasic; | ||
47 | + lwm2m?: Lwm2mSecurityConfigModels; | ||
48 | + }; | ||
41 | attributes: { | 49 | attributes: { |
42 | server: AttributeData[], | 50 | server: AttributeData[], |
43 | shared: AttributeData[] | 51 | shared: AttributeData[] |
@@ -61,6 +69,7 @@ export interface ImportEntitiesResultInfo { | @@ -61,6 +69,7 @@ export interface ImportEntitiesResultInfo { | ||
61 | }; | 69 | }; |
62 | error?: { | 70 | error?: { |
63 | entity: number; | 71 | entity: number; |
72 | + errors?: string; | ||
64 | }; | 73 | }; |
65 | } | 74 | } |
66 | 75 |
@@ -60,6 +60,20 @@ export interface Lwm2mSecurityConfigModels { | @@ -60,6 +60,20 @@ export interface Lwm2mSecurityConfigModels { | ||
60 | bootstrap: BootstrapSecurityConfig; | 60 | bootstrap: BootstrapSecurityConfig; |
61 | } | 61 | } |
62 | 62 | ||
63 | + | ||
64 | +export function getLwm2mSecurityConfigModelsDefault(): Lwm2mSecurityConfigModels { | ||
65 | + return { | ||
66 | + client: { | ||
67 | + securityConfigClientMode: Lwm2mSecurityType.NO_SEC, | ||
68 | + endpoint: '' | ||
69 | + }, | ||
70 | + bootstrap: { | ||
71 | + bootstrapServer: getDefaultServerSecurityConfig(), | ||
72 | + lwm2mServer: getDefaultServerSecurityConfig() | ||
73 | + } | ||
74 | + }; | ||
75 | +} | ||
76 | + | ||
63 | export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig { | 77 | export function getDefaultClientSecurityConfig(securityConfigMode: Lwm2mSecurityType, endPoint = ''): ClientSecurityConfig { |
64 | let security = { | 78 | let security = { |
65 | securityConfigClientMode: securityConfigMode, | 79 | securityConfigClientMode: securityConfigMode, |
@@ -2184,9 +2184,11 @@ | @@ -2184,9 +2184,11 @@ | ||
2184 | "column-title": "Title", | 2184 | "column-title": "Title", |
2185 | "column-example": "Example value data", | 2185 | "column-example": "Example value data", |
2186 | "column-key": "Attribute/telemetry key", | 2186 | "column-key": "Attribute/telemetry key", |
2187 | + "credentials": "Credentials", | ||
2187 | "csv-delimiter": "CSV delimiter", | 2188 | "csv-delimiter": "CSV delimiter", |
2188 | "csv-first-line-header": "First line contains column names", | 2189 | "csv-first-line-header": "First line contains column names", |
2189 | "csv-update-data": "Update attributes/telemetry", | 2190 | "csv-update-data": "Update attributes/telemetry", |
2191 | + "details": "Details", | ||
2190 | "import-csv-number-columns-error": "A file should contain at least two columns", | 2192 | "import-csv-number-columns-error": "A file should contain at least two columns", |
2191 | "import-csv-invalid-format-error": "Invalid file format. Line: '{{line}}'", | 2193 | "import-csv-invalid-format-error": "Invalid file format. Line: '{{line}}'", |
2192 | "column-type": { | 2194 | "column-type": { |
@@ -2200,6 +2202,25 @@ | @@ -2200,6 +2202,25 @@ | ||
2200 | "timeseries": "Timeseries", | 2202 | "timeseries": "Timeseries", |
2201 | "entity-field": "Entity field", | 2203 | "entity-field": "Entity field", |
2202 | "access-token": "Access token", | 2204 | "access-token": "Access token", |
2205 | + "x509": "X.509", | ||
2206 | + "mqtt": { | ||
2207 | + "client-id": "MQTT client ID", | ||
2208 | + "user-name": "MQTT user name", | ||
2209 | + "password": "MQTT password" | ||
2210 | + }, | ||
2211 | + "lwm2m": { | ||
2212 | + "client-endpoint": "LwM2M endpoint client name", | ||
2213 | + "security-config-mode": "LwM2M security config mode", | ||
2214 | + "client-identity": "LwM2M client identity", | ||
2215 | + "client-key": "LwM2M client key", | ||
2216 | + "client-cert": "LwM2M client public key", | ||
2217 | + "bootstrap-server-security-mode": "LwM2M bootstrap server security mode", | ||
2218 | + "bootstrap-server-secret-key": "LwM2M bootstrap server secret key", | ||
2219 | + "bootstrap-server-public-key-id": "LwM2M bootstrap server public key or id", | ||
2220 | + "lwm2m-server-security-mode": "LwM2M server security mode", | ||
2221 | + "lwm2m-server-secret-key": "LwM2M server secret key", | ||
2222 | + "lwm2m-server-public-key-id": "LwM2M server public key or id" | ||
2223 | + }, | ||
2203 | "isgateway": "Is Gateway", | 2224 | "isgateway": "Is Gateway", |
2204 | "activity-time-from-gateway-device": "Activity time from gateway device", | 2225 | "activity-time-from-gateway-device": "Activity time from gateway device", |
2205 | "description": "Description", | 2226 | "description": "Description", |