Commit 41e572490ef373f6d2718f1fe8e8f10b7e986b88

Authored by Viacheslav Klimov
2 parents 33592ca0 e3bf26e2

Merge branch 'feature/bulk-import/device-credentials' of https://github.com/vvll…

…add28/thingsboard into feature/bulk-import
@@ -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",