• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ngageoint / geopackage-js / 4078143969

pending completion
4078143969

push

github

Christopher Caldwell
bump version

3593 of 8015 branches covered (44.83%)

Branch coverage included in aggregate %.

15102 of 20471 relevant lines covered (73.77%)

1564.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.36
/lib/user/userRow.ts
1
import { UserTable } from './userTable';
1✔
2
import { GeoPackageDataType } from '../db/geoPackageDataType';
1✔
3
import { DBValue } from '../db/dbValue';
4
import { UserColumn } from './userColumn';
5
import { UserColumns } from './userColumns';
6
import { GeoPackageException } from '../geoPackageException';
1✔
7
import { ContentValues } from './contentValues';
1✔
8
import { DateConverter } from '../db/dateConverter';
1✔
9

10
export class UserRow<TColumn extends UserColumn, TTable extends UserTable<TColumn>> {
1✔
11
  /**
12
   * User table
13
   */
14
  protected readonly table: TTable;
15

16
  /**
17
   * User columns
18
   */
19
  protected readonly columns: UserColumns<TColumn>;
20

21
  /**
22
   * Column types of this row, based upon the data values
23
   */
24
  protected readonly columnTypes: GeoPackageDataType[];
25

26
  /**
27
   * Array of row values
28
   */
29
  protected readonly values: DBValue[];
30

31
  /**
32
   * Constructor
33
   * @param table
34
   */
35
  constructor(table: TTable);
36

37
  /**
38
   * Constructor
39
   * @param userRow
40
   */
41
  constructor(userRow: UserRow<TColumn, TTable>);
42

43
  /**
44
   * Cosnstructor
45
   * @param table
46
   * @param columns
47
   * @param columnTypes
48
   * @param values
49
   */
50
  constructor(table: TTable, columns: UserColumns<TColumn>, columnTypes: number[], values: DBValue[]);
51

52
  /**
53
   * User Row containing the values from a single result row
54
   * @param args
55
   */
56
  constructor(...args) {
48,087✔
57
    if (args.length === 1) {
8,744✔
58
      if (args[0] instanceof UserTable) {
1,459✔
59
        const table: TTable = args[0] as TTable;
1,175✔
60
        this.table = table;
1,175✔
61
        this.columns = table.getUserColumns();
1,175✔
62
        this.columnTypes = Array(this.columns.getColumns().length)
1,175✔
63
          .fill(0)
64
          .map((_, i) => i);
6,217✔
65
        this.values = Array(this.columns.getColumns().length)
1,175✔
66
          .fill(null)
67
          .map((_, i) => i);
6,217✔
68
        const columns = table.getColumns();
1,175✔
69
        for (let i = 0; i < columns.length; i++) {
1,175✔
70
          const column = columns[i];
6,217✔
71
          this.columnTypes[column.getIndex()] = column.getDataType();
6,217✔
72
          this.values[column.getIndex()] = column.getDefaultValue();
6,217✔
73
        }
74
      } else if (args[0] instanceof UserRow) {
284!
75
        const userRow: UserRow<TColumn, TTable> = args[0];
284✔
76
        this.table = userRow.table;
284✔
77
        this.columns = userRow.columns;
284✔
78
        this.columnTypes = userRow.columnTypes;
284✔
79
        this.values = Array(this.columns.getColumns().length)
284✔
80
          .fill(null)
81
          .map((_, i) => i);
2,272✔
82
        for (let i = 0; i < userRow.values.length; i++) {
284✔
83
          const value = userRow.values[i];
2,272✔
84
          if (value != null) {
2,272✔
85
            const column = userRow.columns.getColumnForIndex(i);
1,844✔
86
            this.values[i] = this.copyValue(column, value);
1,844✔
87
          }
88
        }
89
      }
90
    } else if (args.length === 4) {
7,285!
91
      this.table = args[0];
7,285✔
92
      this.columns = args[1];
7,285✔
93
      this.columnTypes = args[2];
7,285✔
94
      this.values = args[3];
7,285✔
95
    }
96
  }
97

98
  /**
99
   * Get the row values
100
   *
101
   * @return values
102
   */
103
  public getValues(): DBValue[] {
1✔
104
    return this.values;
868✔
105
  }
106

107
  /**
108
   * Get the row column data types
109
   *
110
   * @return row column types
111
   */
112
  public getRowColumnTypes(): number[] {
1✔
113
    return this.columnTypes;
866✔
114
  }
115

116
  /**
117
   * Gets the columns
118
   */
119
  public getColumns(): UserColumns<TColumn> {
1✔
120
    return this.columns;
13,552✔
121
  }
122

123
  /**
124
   * Copy the value of the data type
125
   * @param column table column
126
   * @param value value
127
   * @return copy value
128
   */
129
  protected copyValue(column: TColumn, value: any): any {
1✔
130
    let copyValue = value;
1,844✔
131

132
    switch (column.getDataType()) {
1,844✔
133
      case GeoPackageDataType.BLOB:
1,844!
134
        if (value instanceof Buffer) {
×
135
          copyValue = Buffer.from(value);
×
136
        } else if (value instanceof Uint8Array) {
×
137
          copyValue = Buffer.from(value);
×
138
        } else {
139
          throw new GeoPackageException(
×
140
            'Unsupported copy value type. column: ' + column.getName() + ', data type: ' + column.getDataType(),
141
          );
142
        }
143
        break;
×
144
      case GeoPackageDataType.DATE:
145
      case GeoPackageDataType.DATETIME:
146
        if (value instanceof Date) {
×
147
          copyValue = new Date(value);
×
148
        } else if (!(value instanceof String)) {
×
149
          throw new GeoPackageException(
×
150
            'Unsupported copy value type. column: ' + column.getName() + ', data type: ' + column.getDataType(),
151
          );
152
        }
153
        break;
×
154
      default:
155
    }
156

157
    return copyValue;
1,844✔
158
  }
159

160
  /**
161
   * Gets the id column
162
   * @return {UserColumn}
163
   */
164
  get idColumn(): UserColumn {
1✔
165
    return this.table.getPkColumn();
×
166
  }
167
  /**
168
   * Get the column count
169
   * @return {number} column count
170
   */
171
  get columnCount(): number {
1✔
172
    return this.table.getColumns().length;
2✔
173
  }
174
  /**
175
   * Get the column names
176
   * @return {Array} column names
177
   */
178
  get columnNames(): string[] {
1✔
179
    return this.table.getColumnNames();
4✔
180
  }
181
  /**
182
   * Get the column name at the index
183
   * @param  {Number} index index
184
   * @return {string}       column name
185
   */
186
  getColumnNameWithIndex(index: number): string {
1✔
187
    return this.table.getColumnName(index);
2,232✔
188
  }
189
  /**
190
   * Get the column index of the column name
191
   * @param  {string} columnName column name
192
   * @return {Number}            column index
193
   */
194
  getColumnIndexWithColumnName(columnName: string): number {
1✔
195
    return this.table.getColumnIndex(columnName);
14,097✔
196
  }
197
  /**
198
   * Get the value at the index
199
   * @param  {Number} index index
200
   * @return {object}       value
201
   */
202
  getValueWithIndex(index: number): any {
1✔
203
    let value = this.values[index];
11,060✔
204
    if (value !== undefined) {
11,060✔
205
      value = this.toObjectValue(index, value);
10,976✔
206
    }
207
    return value;
11,060✔
208
  }
209
  /**
210
   * Get the value of the column name
211
   * @param  {string} columnName column name
212
   * @return {Object}            value
213
   */
214
  getValue(columnName: string): any {
1✔
215
    const index = this.getColumnIndexWithColumnName(columnName);
4,598✔
216
    const value = this.values[index];
4,598✔
217
    if (value != null) {
4,598✔
218
      const dataType = this.getRowColumnTypeWithColumnName(columnName);
4,302✔
219
      if (dataType === GeoPackageDataType.BOOLEAN) {
4,302✔
220
        return value === 1;
5✔
221
      }
222
    }
223
    return value;
4,593✔
224
  }
225
  /**
226
   * Get the value from the database as an object based on the column
227
   * @param index column index
228
   * @param value value from the database
229
   */
230
  toObjectValue(index: number, value: DBValue): any {
1✔
231
    const objectValue = value;
10,978✔
232
    const column = this.getColumnWithIndex(index);
10,978✔
233
    if (column.getDataType() === GeoPackageDataType.BOOLEAN && value) {
10,978✔
234
      return value === 1;
1✔
235
    }
236
    return objectValue;
10,977✔
237
  }
238
  /**
239
   * Get the value which will be persisted to the database based on the column
240
   * @param columnName name of the column
241
   */
242
  toDatabaseValue(columnName: string): DBValue {
1✔
243
    const column = this.getColumn(columnName);
×
244
    const value = this.getValue(columnName);
×
245
    if (column.getDataType() === GeoPackageDataType.BOOLEAN) {
×
246
      return value === true ? 1 : 0;
×
247
    }
248
    return value;
×
249
  }
250
  /**
251
   * Get the row column type at the index
252
   * @param  {Number} index index
253
   * @return {Number}       row column type
254
   */
255
  getRowColumnTypeWithIndex(index: number): GeoPackageDataType {
1✔
256
    return this.columnTypes[index];
4,901✔
257
  }
258
  /**
259
   * Get the row column type of the column name
260
   * @param  {string} columnName column name
261
   * @return {Number}            row column type
262
   */
263
  getRowColumnTypeWithColumnName(columnName: string): number {
1✔
264
    return this.columnTypes[this.getColumnIndexWithColumnName(columnName)];
4,304✔
265
  }
266
  /**
267
   * Get the column at the index
268
   * @param  {Number} index index
269
   * @return {UserColumn}       column
270
   */
271
  getColumnWithIndex(index: number): UserColumn {
1✔
272
    return this.table.getColumnForIndex(index);
10,982✔
273
  }
274
  /**
275
   * Get the column of the column name
276
   * @param  {string} columnName column name
277
   * @return {UserColumn}            column
278
   */
279
  getColumn(columnName: string): UserColumn {
1✔
280
    return this.table.getColumn(columnName);
2✔
281
  }
282

283
  /**
284
   * Get the primary key column Index
285
   * @return {Number} pk index
286
   */
287
  get pkColumnIndex(): number {
1✔
288
    return this.table.getUserColumns().getPkColumnIndex();
×
289
  }
290
  /**
291
   * Get the primary key column
292
   * @return {UserColumn} pk column
293
   */
294
  get pkColumn(): UserColumn {
1✔
295
    return this.table.getPkColumn();
2✔
296
  }
297
  /**
298
   * Set the value at the index
299
   * @param {Number} index index
300
   * @param {object} value value
301
   */
302
  setValueWithIndex(index: number, value: any): void {
1✔
303
    if (index === this.table.getUserColumns().getPkColumnIndex()) {
4,899!
304
      throw new GeoPackageException(
×
305
        'Cannot update the primary key of the row.  Table Name: ' +
306
          this.table.getTableName() +
307
          ', Index: ' +
308
          index +
309
          ', Name: ' +
310
          this.table.getPkColumnName(),
311
      );
312
    }
313
    const dataType = this.getRowColumnTypeWithIndex(index);
4,899✔
314
    if (dataType === GeoPackageDataType.BOOLEAN) {
4,899✔
315
      value === true ? (this.values[index] = 1) : (this.values[index] = 0);
101✔
316
    } else if (dataType === GeoPackageDataType.DATE) {
4,798✔
317
      this.values[index] = value.toISOString().slice(0, 10);
93✔
318
    } else if (dataType === GeoPackageDataType.DATETIME) {
4,705✔
319
      this.values[index] = value.toISOString();
150✔
320
    } else {
321
      this.values[index] = value;
4,555✔
322
    }
323
  }
324
  /**
325
   * Set the value at the index without validation
326
   * @param {Number} index index
327
   * @param {Object} value value
328
   */
329
  setValueNoValidationWithIndex(index: number, value: any): void {
1✔
330
    this.values[index] = value;
×
331
  }
332
  /**
333
   * Set the value of the column name
334
   * @param {string} columnName column name
335
   * @param {Object} value      value
336
   */
337
  setValue(columnName: string, value: any): void {
1✔
338
    const columnIndex = this.getColumnIndexWithColumnName(columnName);
2,963✔
339
    this.setValueWithIndex(columnIndex, value);
2,963✔
340
  }
341

342
  /**
343
   * Get the id value, which is the value of the primary key
344
   *
345
   * @return id
346
   */
347
  public getId(): number {
1✔
348
    let id;
349
    const index = this.columns.getPkColumnIndex();
5,062✔
350
    if (index < 0) {
5,062!
351
      const error = ['Id column does not exist in '];
×
352
      if (this.columns.isCustom()) {
×
353
        error.push('custom specified table columns. ');
×
354
      }
355
      error.push('table: ' + this.columns.getTableName());
×
356
      if (this.columns.isCustom()) {
×
357
        error.push(', columns: ' + this.columns.getColumnNames());
×
358
      }
359
      throw new GeoPackageException(error.join(''));
×
360
    }
361
    const objectValue = this.getValueWithIndex(index);
5,062✔
362
    if (objectValue == null) {
5,062!
363
      throw new GeoPackageException(
×
364
        'Row Id was null. table: ' +
365
          this.columns.getTableName() +
366
          ', index: ' +
367
          index +
368
          ', name: ' +
369
          this.columns.getPkColumn().getName(),
370
      );
371
    }
372
    if (typeof objectValue === 'number') {
5,062!
373
      id = objectValue;
5,062✔
374
    } else {
375
      throw new GeoPackageException(
×
376
        'Row Id was not a number. table: ' +
377
          this.columns.getTableName() +
378
          ', index: ' +
379
          index +
380
          ', name: ' +
381
          this.columns.getPkColumn().getName() +
382
          ', value: ' +
383
          objectValue,
384
      );
385
    }
386
    return id;
5,062✔
387
  }
388
  hasIdColumn(): boolean {
1✔
389
    return this.table.getUserColumns().getPkColumnIndex() !== undefined;
1,232✔
390
  }
391
  hasId(): boolean {
1✔
392
    let hasId = false;
184✔
393
    if (this.hasIdColumn()) {
184!
394
      const objectValue = this.getValueWithIndex(this.table.getUserColumns().getPkColumnIndex());
184✔
395
      hasId = objectValue !== null && objectValue !== undefined && typeof objectValue === 'number';
184✔
396
    }
397
    return hasId;
184✔
398
  }
399
  /**
400
   * Clears the id so the row can be used as part of an insert or create
401
   */
402
  resetId(): void {
1✔
403
    this.values[this.table.getPkColumnName()] = undefined;
2✔
404
  }
405

406
  /**
407
   * Set the id and optionally validate
408
   * @param id id value
409
   * @param pkModifiable primary key modifiable
410
   */
411
  public setId(id: number, pkModifiable = this.columns.isPkModifiable()): void {
1,096!
412
    const index = this.columns.getPkColumnIndex();
1,096✔
413
    if (index >= 0) {
1,096✔
414
      if (!pkModifiable) {
885!
415
        throw new GeoPackageException(
×
416
          'Can not update the primary key of the row. Table Name: ' +
417
            this.table.getTableName() +
418
            ', Index: ' +
419
            index +
420
            ', Name: ' +
421
            this.table.getPkColumnName(),
422
        );
423
      }
424
      this.values[index] = id;
885✔
425
    }
426
  }
427

428
  /**
429
   * Return the table
430
   */
431
  public getTable(): TTable {
1✔
432
    return this.table;
8,317✔
433
  }
434

435
  /**
436
   * Convert the row to content values
437
   * @param {boolean} includeNulls include null values (default is true)
438
   * @return {ContentValues} content values
439
   */
440
  public toContentValues(includeNulls = true): ContentValues {
1,168✔
441
    const contentValues = new ContentValues();
1,158✔
442
    for (const column of this.columns.getColumns()) {
6,132✔
443
      const value = this.values[column.getIndex()];
6,132✔
444
      if (!column.isPrimaryKey() || (value != null && this.columns.isPkModifiable())) {
6,132✔
445
        const columnName = column.getName();
5,185✔
446
        if (value != null) {
5,185✔
447
          this.columnToContentValue(contentValues, column, value);
4,850✔
448
        } else if (includeNulls) {
335✔
449
          contentValues.putNull(columnName);
200✔
450
        }
451
      }
452
    }
453
    if (contentValues.size() == 0) {
1,158!
454
      for (const column of this.columns.getColumns()) {
×
455
        if (!column.isPrimaryKey()) {
×
456
          contentValues.putNull(column.getName());
×
457
        }
458
      }
459
    }
460
    return contentValues;
1,158✔
461
  }
462

463
  /**
464
   * Map the column to the content values
465
   * @param contentValues content values
466
   * @param column column
467
   * @param value value
468
   */
469
  protected columnToContentValue(contentValues: ContentValues, column: TColumn, value: any): void {
1✔
470
    const columnName = column.getName();
4,561✔
471
    if (typeof value === 'number') {
4,561✔
472
      this.validateValue(column, value, ['number']);
2,126✔
473
      contentValues.put(columnName, value);
2,126✔
474
    } else if (typeof value === 'string') {
2,435✔
475
      this.validateValue(column, value, ['Date', 'string']);
1,807✔
476
      const stringValue = value as string;
1,807✔
477
      if (column.getMax() != null && stringValue.length > column.getMax()) {
1,807!
478
        throw new GeoPackageException(
×
479
          'String is larger than the column max. Size: ' +
480
            stringValue.length +
481
            ', Max: ' +
482
            column.getMax() +
483
            ', Column: ' +
484
            columnName,
485
        );
486
      }
487
      contentValues.put(columnName, stringValue);
1,807✔
488
    } else if (value instanceof Buffer || value instanceof Uint8Array) {
628!
489
      this.validateValue(column, value, ['Buffer']);
628✔
490
      if (column.getMax() != null && value.length > column.getMax()) {
628!
491
        throw new GeoPackageException(
×
492
          'Byte array is larger than the column max. Size: ' +
493
            value.length +
494
            ', Max: ' +
495
            column.getMax() +
496
            ', Column: ' +
497
            columnName,
498
        );
499
      }
500
      contentValues.put(columnName, value);
628✔
501
    } else if (typeof value === 'boolean') {
×
502
      this.validateValue(column, value, ['boolean']);
×
503
      contentValues.put(columnName, value ? 1 : 0);
×
504
    } else if (value instanceof Date) {
×
505
      this.validateValue(column, value, ['Date', 'string']);
×
506
      const dateString = DateConverter.stringValue(value, column.getDataType());
×
507
      contentValues.put(columnName, dateString);
×
508
    } else {
509
      throw new GeoPackageException('Unsupported update column value. column: ' + columnName + ', value: ' + value);
×
510
    }
511
  }
512

513
  /**
514
   * Validate the value and its actual value types against the column data type class
515
   * @param column column
516
   * @param value value
517
   * @param valueTypes value type
518
   */
519
  protected validateValue(column: TColumn, value: any, valueTypes: string[]): void {
1✔
520
    if (this.columns.isValueValidation()) {
4,561!
521
      const dataType = column.getDataType();
4,561✔
522
      if (dataType == null) {
4,561!
523
        throw new GeoPackageException(
×
524
          'Column is missing a data type. Column: ' +
525
            column.getName() +
526
            ', Value: ' +
527
            value +
528
            ", Type: '" +
529
            column.getType() +
530
            "', Actual Type: " +
531
            valueTypes[0],
532
        );
533
      }
534
      const dataTypeClass = GeoPackageDataType.getClass(dataType);
4,561✔
535
      let valid = false;
4,561✔
536
      for (const valueType of valueTypes) {
6,101✔
537
        if (valueType === dataTypeClass) {
6,101✔
538
          valid = true;
4,561✔
539
          break;
4,561✔
540
        }
541
      }
542
      if (!valid) {
4,561!
543
        throw new GeoPackageException(
×
544
          'Illegal value. Column: ' +
545
            column.getName() +
546
            ', Value: ' +
547
            value +
548
            ', Expected Type: ' +
549
            dataTypeClass +
550
            ', Actual Type: ' +
551
            valueTypes[0],
552
        );
553
      }
554
    }
555
  }
556
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc