@squiz/db-lib 1.2.1-alpha.95 → 1.2.1-alpha.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/lib/AbstractRepository.d.ts +5 -3
  3. package/lib/PostgresErrorCodes.d.ts +268 -0
  4. package/lib/assertions/assertAssign.d.ts +4 -0
  5. package/lib/assertions/assertAssign.spec.d.ts +1 -0
  6. package/lib/assertions/assertAssignWithDefaultUndefinedValue.d.ts +4 -0
  7. package/lib/assertions/assertAssignWithDefaultUndefinedValue.spec.d.ts +1 -0
  8. package/lib/assertions/assertIsDefined.d.ts +1 -0
  9. package/lib/assertions/assertIsDefined.spec.d.ts +1 -0
  10. package/lib/assertions/assertIsMapOfStringString.d.ts +3 -0
  11. package/lib/assertions/assertIsMapOfStringString.spec.d.ts +1 -0
  12. package/lib/assertions/assertIsNotAnEmptyString.d.ts +1 -0
  13. package/lib/assertions/assertIsNotAnEmptyString.spec.d.ts +1 -0
  14. package/lib/assertions/assertIsObject.d.ts +1 -0
  15. package/lib/assertions/assertIsObject.spec.d.ts +1 -0
  16. package/lib/assertions/assertIsString.d.ts +1 -0
  17. package/lib/assertions/assertIsString.spec.d.ts +1 -0
  18. package/lib/index.d.ts +8 -0
  19. package/lib/index.js +12369 -1364
  20. package/lib/index.js.map +4 -4
  21. package/package.json +10 -9
  22. package/src/AbstractRepository.ts +11 -7
  23. package/src/Migrator.ts +13 -9
  24. package/src/PostgresErrorCodes.ts +270 -0
  25. package/src/assertions/assertAssign.spec.ts +18 -0
  26. package/src/assertions/assertAssign.ts +22 -0
  27. package/src/assertions/assertAssignWithDefaultUndefinedValue.spec.ts +31 -0
  28. package/src/assertions/assertAssignWithDefaultUndefinedValue.ts +17 -0
  29. package/src/assertions/assertIsDefined.spec.ts +19 -0
  30. package/src/assertions/assertIsDefined.ts +7 -0
  31. package/src/assertions/assertIsMapOfStringString.spec.ts +23 -0
  32. package/src/assertions/assertIsMapOfStringString.ts +29 -0
  33. package/src/assertions/assertIsNotAnEmptyString.spec.ts +25 -0
  34. package/src/assertions/assertIsNotAnEmptyString.ts +17 -0
  35. package/src/assertions/assertIsObject.spec.ts +17 -0
  36. package/src/assertions/assertIsObject.ts +11 -0
  37. package/src/assertions/assertIsString.spec.ts +16 -0
  38. package/src/assertions/assertIsString.ts +11 -0
  39. package/src/index.ts +14 -0
  40. package/tsconfig.json +5 -1
  41. package/tsconfig.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@squiz/db-lib",
3
- "version": "1.2.1-alpha.95",
3
+ "version": "1.2.1-alpha.97",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
7
7
  "start": "node ./lib/index.js",
8
8
  "compile": "node build.js && npx tsc",
9
9
  "lint": "eslint ./src --ext .ts",
10
- "test": "echo \"tested in integration\"",
10
+ "test": "jest -c jest.config.ts",
11
11
  "test:integration___": "jest -c jest.config.ts",
12
12
  "test:update-snapshots": "jest -c jest.config.ts --updateSnapshot",
13
13
  "clean": "rimraf \".tsbuildinfo\" \"./lib\""
@@ -18,20 +18,21 @@
18
18
  "@types/jest": "28.1.6",
19
19
  "@types/node": "17.0.27",
20
20
  "@types/pg": "8.6.5",
21
- "esbuild": "0.14.49",
22
- "eslint": "8.18.0",
21
+ "esbuild": "0.15.3",
22
+ "eslint": "8.22.0",
23
23
  "fs-extra": "10.1.0",
24
24
  "jest": "28.1.3",
25
25
  "rimraf": "3.0.2",
26
- "ts-jest": "28.0.5",
26
+ "ts-jest": "28.0.7",
27
27
  "ts-loader": "9.3.1",
28
- "ts-node": "10.8.1",
28
+ "ts-node": "10.9.1",
29
29
  "typescript": "4.7.4"
30
30
  },
31
31
  "dependencies": {
32
- "@aws-sdk/client-secrets-manager": "^3.121.0",
32
+ "@aws-sdk/client-secrets-manager": "3.121.0",
33
+ "@squiz/dx-logger-lib": "^1.2.1-alpha.97",
33
34
  "dotenv": "16.0.1",
34
- "pg": "^8.7.3"
35
+ "pg": "8.7.3"
35
36
  },
36
- "gitHead": "3b3b33ee15543041c29262becfc9cd3674ddc111"
37
+ "gitHead": "bf3e5580c5b09c3e46a5965da38e4ed4657463d4"
37
38
  }
@@ -17,24 +17,27 @@ export type Repository<T> = Reader<T> & Writer<T>;
17
17
  export abstract class AbstractRepository<T> implements Reader<T>, Writer<T> {
18
18
  protected tableName: string;
19
19
 
20
+ /** object where the key is the model property name amd the value is sql column name */
20
21
  protected modelPropertyToSqlColumn: { [key in keyof T]: string };
21
- protected sqlColumnToModelProperty: { [key: string]: keyof T };
22
+ /** object where the key is the sql column name and the value is the model property name */
23
+ protected sqlColumnToModelProperty: { [key: string]: string };
22
24
 
23
25
  constructor(
24
26
  protected repositories: Repositories,
25
27
  protected pool: Pool,
26
28
  tableName: string,
27
29
  mapping: { [key in keyof T]: string },
28
- protected classRef: { new (): T },
30
+ protected classRef: { new (data?: Record<string, unknown>): T },
29
31
  ) {
30
32
  this.tableName = `"${tableName}"`;
31
33
 
32
34
  this.modelPropertyToSqlColumn = mapping;
35
+
33
36
  this.sqlColumnToModelProperty = Object.entries(mapping).reduce((prev, curr) => {
34
- const [modelProp, columnName] = curr as [keyof T, string];
37
+ const [modelProp, columnName] = curr as [string, string];
35
38
  prev[columnName] = modelProp;
36
39
  return prev;
37
- }, {} as { [key: string]: keyof T });
40
+ }, {} as { [key: string]: string });
38
41
  }
39
42
 
40
43
  protected async getConnection(): Promise<PoolClient> {
@@ -131,13 +134,14 @@ export abstract class AbstractRepository<T> implements Reader<T>, Writer<T> {
131
134
  }
132
135
 
133
136
  protected createAndHydrateModel(row: any): T {
134
- const model = new this.classRef();
137
+ const inputData: Record<string, unknown> = {};
135
138
 
136
139
  for (const key of Object.keys(row)) {
137
- model[this.sqlColumnToModelProperty[key]] = row[key];
140
+ const translatedKey = this.sqlColumnToModelProperty[key];
141
+ inputData[translatedKey] = row[key];
138
142
  }
139
143
 
140
- return model;
144
+ return new this.classRef(inputData);
141
145
  }
142
146
 
143
147
  async findOne(item: Partial<T>): Promise<T | undefined> {
package/src/Migrator.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  import fs from 'fs/promises';
2
2
  import { PoolClient } from 'pg';
3
3
  import path from 'path';
4
+ import os from 'os';
5
+ import { getLogger } from '@squiz/dx-logger-lib';
4
6
 
5
7
  // Be carful about changing this value. This needs to be consistent across deployments
6
8
  // this ID is used to signal other running instances that a migration is executing.
7
9
  const MIGRATION_ADVISORY_LOCK = 4569465;
8
10
 
11
+ const logger = getLogger({ name: 'db-migrator', meta: { pid: process.pid, hostname: os.hostname() } });
12
+
9
13
  export class Migrator {
10
14
  constructor(protected migrationDir: string, protected migrationList: string[], protected pool: PoolClient) {}
11
15
 
@@ -25,15 +29,15 @@ export class Migrator {
25
29
  protected async applyMigration(migration: string, sql: string) {
26
30
  try {
27
31
  const result = await this.pool.query(sql);
28
- console.log(process.pid, 'Applying ' + migration);
32
+ logger.info('Applying ' + migration);
29
33
 
30
34
  if (result.rowCount !== undefined) {
31
- console.log(process.pid, 'affected rows', result.rowCount);
35
+ logger.info('affected rows', result.rowCount);
32
36
  }
33
37
 
34
38
  await this.pool.query('insert into __migrations__ (id) values ($1)', [migration]);
35
39
  } catch (e) {
36
- console.log(process.pid, 'error occurred running migration', migration, e);
40
+ logger.info('error occurred running migration', migration, e);
37
41
  throw e;
38
42
  }
39
43
  }
@@ -79,18 +83,18 @@ export class Migrator {
79
83
  const lockObtained = await this.tryToObtainLock();
80
84
 
81
85
  if (lockObtained === false) {
82
- console.log(process.pid, 'migration already running');
86
+ logger.info('migration already running');
83
87
  await sleep(500);
84
88
  return await this.migrate();
85
89
  }
86
90
 
87
91
  await this.runMigrations();
88
- console.log(process.pid, 'completed migration');
92
+ logger.info('completed migration');
89
93
  await this.releaseLock();
90
94
 
91
95
  this.dispose();
92
96
  } catch (e) {
93
- console.log(process.pid, 'migration failed releasing lock');
97
+ logger.info('migration failed releasing lock');
94
98
  await this.releaseLock();
95
99
  throw e;
96
100
  }
@@ -101,11 +105,11 @@ export class Migrator {
101
105
  const pending = await this.getPending(this.migrationList, appliedMigrations);
102
106
 
103
107
  if (pending.length === 0) {
104
- console.log(process.pid, 'No pending migrations');
108
+ logger.info('No pending migrations');
105
109
  return;
106
110
  }
107
111
 
108
- console.log(process.pid, 'Pending migrations:\n\t', pending.join('\n\t'));
112
+ logger.info('Pending migrations:\n\t', pending.join('\n\t'));
109
113
 
110
114
  for (const migration of pending) {
111
115
  await this.runMigration(migration);
@@ -121,7 +125,7 @@ export class Migrator {
121
125
 
122
126
  await this.pool.query('COMMIT');
123
127
  } catch (e) {
124
- console.log(process.pid, 'migration failed', migration, e);
128
+ logger.error('migration failed', migration, e);
125
129
  await this.pool.query('ROLLBACK');
126
130
  throw e;
127
131
  }
@@ -0,0 +1,270 @@
1
+ // See https://github.com/postgres/postgres/blob/master/src/backend/utils/errcodes.txt
2
+
3
+ export enum PostgresErrorCode {
4
+ SUCCESSFUL_COMPLETION = '00000',
5
+ WARNING = '01000',
6
+ WARNING_DYNAMIC_RESULT_SETS_RETURNED = '0100C',
7
+ WARNING_IMPLICIT_ZERO_BIT_PADDING = '01008',
8
+ WARNING_NULL_VALUE_ELIMINATED_IN_SET_FUNCTION = '01003',
9
+ WARNING_PRIVILEGE_NOT_GRANTED = '01007',
10
+ WARNING_PRIVILEGE_NOT_REVOKED = '01006',
11
+ WARNING_STRING_DATA_RIGHT_TRUNCATION = '01004',
12
+ WARNING_DEPRECATED_FEATURE = '01P01',
13
+ NO_DATA = '02000',
14
+ NO_ADDITIONAL_DYNAMIC_RESULT_SETS_RETURNED = '02001',
15
+ SQL_STATEMENT_NOT_YET_COMPLETE = '03000',
16
+ CONNECTION_EXCEPTION = '08000',
17
+ CONNECTION_DOES_NOT_EXIST = '08003',
18
+ CONNECTION_FAILURE = '08006',
19
+ SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION = '08001',
20
+ SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION = '08004',
21
+ TRANSACTION_RESOLUTION_UNKNOWN = '08007',
22
+ PROTOCOL_VIOLATION = '08P01',
23
+ TRIGGERED_ACTION_EXCEPTION = '09000',
24
+ FEATURE_NOT_SUPPORTED = '0A000',
25
+ INVALID_TRANSACTION_INITIATION = '0B000',
26
+ LOCATOR_EXCEPTION = '0F000',
27
+ L_E_INVALID_SPECIFICATION = '0F001',
28
+ INVALID_GRANTOR = '0L000',
29
+ INVALID_GRANT_OPERATION = '0LP01',
30
+ INVALID_ROLE_SPECIFICATION = '0P000',
31
+ DIAGNOSTICS_EXCEPTION = '0Z000',
32
+ STACKED_DIAGNOSTICS_ACCESSED_WITHOUT_ACTIVE_HANDLER = '0Z002',
33
+ CASE_NOT_FOUND = '20000',
34
+ CARDINALITY_VIOLATION = '21000',
35
+ DATA_EXCEPTION = '22000',
36
+ ARRAY_ELEMENT_ERROR = '2202E',
37
+ ARRAY_SUBSCRIPT_ERROR = '2202E',
38
+ CHARACTER_NOT_IN_REPERTOIRE = '22021',
39
+ DATETIME_FIELD_OVERFLOW = '22008',
40
+ DATETIME_VALUE_OUT_OF_RANGE = '22008',
41
+ DIVISION_BY_ZERO = '22012',
42
+ ERROR_IN_ASSIGNMENT = '22005',
43
+ ESCAPE_CHARACTER_CONFLICT = '2200B',
44
+ INDICATOR_OVERFLOW = '22022',
45
+ INTERVAL_FIELD_OVERFLOW = '22015',
46
+ INVALID_ARGUMENT_FOR_LOG = '2201E',
47
+ INVALID_ARGUMENT_FOR_NTILE = '22014',
48
+ INVALID_ARGUMENT_FOR_NTH_VALUE = '22016',
49
+ INVALID_ARGUMENT_FOR_POWER_FUNCTION = '2201F',
50
+ INVALID_ARGUMENT_FOR_WIDTH_BUCKET_FUNCTION = '2201G',
51
+ INVALID_CHARACTER_VALUE_FOR_CAST = '22018',
52
+ INVALID_DATETIME_FORMAT = '22007',
53
+ INVALID_ESCAPE_CHARACTER = '22019',
54
+ INVALID_ESCAPE_OCTET = '2200D',
55
+ INVALID_ESCAPE_SEQUENCE = '22025',
56
+ NONSTANDARD_USE_OF_ESCAPE_CHARACTER = '22P06',
57
+ INVALID_INDICATOR_PARAMETER_VALUE = '22010',
58
+ INVALID_PARAMETER_VALUE = '22023',
59
+ INVALID_PRECEDING_OR_FOLLOWING_SIZE = '22013',
60
+ INVALID_REGULAR_EXPRESSION = '2201B',
61
+ INVALID_ROW_COUNT_IN_LIMIT_CLAUSE = '2201W',
62
+ INVALID_ROW_COUNT_IN_RESULT_OFFSET_CLAUSE = '2201X',
63
+ INVALID_TABLESAMPLE_ARGUMENT = '2202H',
64
+ INVALID_TABLESAMPLE_REPEAT = '2202G',
65
+ INVALID_TIME_ZONE_DISPLACEMENT_VALUE = '22009',
66
+ INVALID_USE_OF_ESCAPE_CHARACTER = '2200C',
67
+ MOST_SPECIFIC_TYPE_MISMATCH = '2200G',
68
+ NULL_VALUE_NOT_ALLOWED = '22004',
69
+ NULL_VALUE_NO_INDICATOR_PARAMETER = '22002',
70
+ NUMERIC_VALUE_OUT_OF_RANGE = '22003',
71
+ SEQUENCE_GENERATOR_LIMIT_EXCEEDED = '2200H',
72
+ STRING_DATA_LENGTH_MISMATCH = '22026',
73
+ STRING_DATA_RIGHT_TRUNCATION = '22001',
74
+ SUBSTRING_ERROR = '22011',
75
+ TRIM_ERROR = '22027',
76
+ UNTERMINATED_C_STRING = '22024',
77
+ ZERO_LENGTH_CHARACTER_STRING = '2200F',
78
+ FLOATING_POINT_EXCEPTION = '22P01',
79
+ INVALID_TEXT_REPRESENTATION = '22P02',
80
+ INVALID_BINARY_REPRESENTATION = '22P03',
81
+ BAD_COPY_FILE_FORMAT = '22P04',
82
+ UNTRANSLATABLE_CHARACTER = '22P05',
83
+ NOT_AN_XML_DOCUMENT = '2200L',
84
+ INVALID_XML_DOCUMENT = '2200M',
85
+ INVALID_XML_CONTENT = '2200N',
86
+ INVALID_XML_COMMENT = '2200S',
87
+ INVALID_XML_PROCESSING_INSTRUCTION = '2200T',
88
+ DUPLICATE_JSON_OBJECT_KEY_VALUE = '22030',
89
+ INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION = '22031',
90
+ INVALID_JSON_TEXT = '22032',
91
+ INVALID_SQL_JSON_SUBSCRIPT = '22033',
92
+ MORE_THAN_ONE_SQL_JSON_ITEM = '22034',
93
+ NO_SQL_JSON_ITEM = '22035',
94
+ NON_NUMERIC_SQL_JSON_ITEM = '22036',
95
+ NON_UNIQUE_KEYS_IN_A_JSON_OBJECT = '22037',
96
+ SINGLETON_SQL_JSON_ITEM_REQUIRED = '22038',
97
+ SQL_JSON_ARRAY_NOT_FOUND = '22039',
98
+ SQL_JSON_MEMBER_NOT_FOUND = '2203A',
99
+ SQL_JSON_NUMBER_NOT_FOUND = '2203B',
100
+ SQL_JSON_OBJECT_NOT_FOUND = '2203C',
101
+ TOO_MANY_JSON_ARRAY_ELEMENTS = '2203D',
102
+ TOO_MANY_JSON_OBJECT_MEMBERS = '2203E',
103
+ SQL_JSON_SCALAR_REQUIRED = '2203F',
104
+ SQL_JSON_ITEM_CANNOT_BE_CAST_TO_TARGET_TYPE = '2203G',
105
+ INTEGRITY_CONSTRAINT_VIOLATION = '23000',
106
+ RESTRICT_VIOLATION = '23001',
107
+ NOT_NULL_VIOLATION = '23502',
108
+ FOREIGN_KEY_VIOLATION = '23503',
109
+ UNIQUE_VIOLATION = '23505',
110
+ CHECK_VIOLATION = '23514',
111
+ EXCLUSION_VIOLATION = '23P01',
112
+ INVALID_CURSOR_STATE = '24000',
113
+ INVALID_TRANSACTION_STATE = '25000',
114
+ ACTIVE_SQL_TRANSACTION = '25001',
115
+ BRANCH_TRANSACTION_ALREADY_ACTIVE = '25002',
116
+ HELD_CURSOR_REQUIRES_SAME_ISOLATION_LEVEL = '25008',
117
+ INAPPROPRIATE_ACCESS_MODE_FOR_BRANCH_TRANSACTION = '25003',
118
+ INAPPROPRIATE_ISOLATION_LEVEL_FOR_BRANCH_TRANSACTION = '25004',
119
+ NO_ACTIVE_SQL_TRANSACTION_FOR_BRANCH_TRANSACTION = '25005',
120
+ READ_ONLY_SQL_TRANSACTION = '25006',
121
+ SCHEMA_AND_DATA_STATEMENT_MIXING_NOT_SUPPORTED = '25007',
122
+ NO_ACTIVE_SQL_TRANSACTION = '25P01',
123
+ IN_FAILED_SQL_TRANSACTION = '25P02',
124
+ IDLE_IN_TRANSACTION_SESSION_TIMEOUT = '25P03',
125
+ INVALID_SQL_STATEMENT_NAME = '26000',
126
+ TRIGGERED_DATA_CHANGE_VIOLATION = '27000',
127
+ INVALID_AUTHORIZATION_SPECIFICATION = '28000',
128
+ INVALID_PASSWORD = '28P01',
129
+ DEPENDENT_PRIVILEGE_DESCRIPTORS_STILL_EXIST = '2B000',
130
+ DEPENDENT_OBJECTS_STILL_EXIST = '2BP01',
131
+ INVALID_TRANSACTION_TERMINATION = '2D000',
132
+ SQL_ROUTINE_EXCEPTION = '2F000',
133
+ S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT = '2F005',
134
+ S_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED = '2F002',
135
+ S_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED = '2F003',
136
+ S_R_E_READING_SQL_DATA_NOT_PERMITTED = '2F004',
137
+ INVALID_CURSOR_NAME = '34000',
138
+ EXTERNAL_ROUTINE_EXCEPTION = '38000',
139
+ E_R_E_CONTAINING_SQL_NOT_PERMITTED = '38001',
140
+ E_R_E_MODIFYING_SQL_DATA_NOT_PERMITTED = '38002',
141
+ E_R_E_PROHIBITED_SQL_STATEMENT_ATTEMPTED = '38003',
142
+ E_R_E_READING_SQL_DATA_NOT_PERMITTED = '38004',
143
+ EXTERNAL_ROUTINE_INVOCATION_EXCEPTION = '39000',
144
+ E_R_I_E_INVALID_SQLSTATE_RETURNED = '39001',
145
+ E_R_I_E_NULL_VALUE_NOT_ALLOWED = '39004',
146
+ E_R_I_E_TRIGGER_PROTOCOL_VIOLATED = '39P01',
147
+ E_R_I_E_SRF_PROTOCOL_VIOLATED = '39P02',
148
+ E_R_I_E_EVENT_TRIGGER_PROTOCOL_VIOLATED = '39P03',
149
+ SAVEPOINT_EXCEPTION = '3B000',
150
+ S_E_INVALID_SPECIFICATION = '3B001',
151
+ INVALID_CATALOG_NAME = '3D000',
152
+ INVALID_SCHEMA_NAME = '3F000',
153
+ TRANSACTION_ROLLBACK = '40000',
154
+ T_R_INTEGRITY_CONSTRAINT_VIOLATION = '40002',
155
+ T_R_SERIALIZATION_FAILURE = '40001',
156
+ T_R_STATEMENT_COMPLETION_UNKNOWN = '40003',
157
+ T_R_DEADLOCK_DETECTED = '40P01',
158
+ SYNTAX_ERROR_OR_ACCESS_RULE_VIOLATION = '42000',
159
+ SYNTAX_ERROR = '42601',
160
+ INSUFFICIENT_PRIVILEGE = '42501',
161
+ CANNOT_COERCE = '42846',
162
+ GROUPING_ERROR = '42803',
163
+ WINDOWING_ERROR = '42P20',
164
+ INVALID_RECURSION = '42P19',
165
+ INVALID_FOREIGN_KEY = '42830',
166
+ INVALID_NAME = '42602',
167
+ NAME_TOO_LONG = '42622',
168
+ RESERVED_NAME = '42939',
169
+ DATATYPE_MISMATCH = '42804',
170
+ INDETERMINATE_DATATYPE = '42P18',
171
+ COLLATION_MISMATCH = '42P21',
172
+ INDETERMINATE_COLLATION = '42P22',
173
+ WRONG_OBJECT_TYPE = '42809',
174
+ GENERATED_ALWAYS = '428C9',
175
+ UNDEFINED_COLUMN = '42703',
176
+ UNDEFINED_CURSOR = '34000',
177
+ UNDEFINED_DATABASE = '3D000',
178
+ UNDEFINED_FUNCTION = '42883',
179
+ UNDEFINED_PSTATEMENT = '26000',
180
+ UNDEFINED_SCHEMA = '3F000',
181
+ UNDEFINED_TABLE = '42P01',
182
+ UNDEFINED_PARAMETER = '42P02',
183
+ UNDEFINED_OBJECT = '42704',
184
+ DUPLICATE_COLUMN = '42701',
185
+ DUPLICATE_CURSOR = '42P03',
186
+ DUPLICATE_DATABASE = '42P04',
187
+ DUPLICATE_FUNCTION = '42723',
188
+ DUPLICATE_PSTATEMENT = '42P05',
189
+ DUPLICATE_SCHEMA = '42P06',
190
+ DUPLICATE_TABLE = '42P07',
191
+ DUPLICATE_ALIAS = '42712',
192
+ DUPLICATE_OBJECT = '42710',
193
+ AMBIGUOUS_COLUMN = '42702',
194
+ AMBIGUOUS_FUNCTION = '42725',
195
+ AMBIGUOUS_PARAMETER = '42P08',
196
+ AMBIGUOUS_ALIAS = '42P09',
197
+ INVALID_COLUMN_REFERENCE = '42P10',
198
+ INVALID_COLUMN_DEFINITION = '42611',
199
+ INVALID_CURSOR_DEFINITION = '42P11',
200
+ INVALID_DATABASE_DEFINITION = '42P12',
201
+ INVALID_FUNCTION_DEFINITION = '42P13',
202
+ INVALID_PSTATEMENT_DEFINITION = '42P14',
203
+ INVALID_SCHEMA_DEFINITION = '42P15',
204
+ INVALID_TABLE_DEFINITION = '42P16',
205
+ INVALID_OBJECT_DEFINITION = '42P17',
206
+ WITH_CHECK_OPTION_VIOLATION = '44000',
207
+ INSUFFICIENT_RESOURCES = '53000',
208
+ DISK_FULL = '53100',
209
+ OUT_OF_MEMORY = '53200',
210
+ TOO_MANY_CONNECTIONS = '53300',
211
+ CONFIGURATION_LIMIT_EXCEEDED = '53400',
212
+ PROGRAM_LIMIT_EXCEEDED = '54000',
213
+ STATEMENT_TOO_COMPLEX = '54001',
214
+ TOO_MANY_COLUMNS = '54011',
215
+ TOO_MANY_ARGUMENTS = '54023',
216
+ OBJECT_NOT_IN_PREREQUISITE_STATE = '55000',
217
+ OBJECT_IN_USE = '55006',
218
+ CANT_CHANGE_RUNTIME_PARAM = '55P02',
219
+ LOCK_NOT_AVAILABLE = '55P03',
220
+ UNSAFE_NEW_ENUM_VALUE_USAGE = '55P04',
221
+ OPERATOR_INTERVENTION = '57000',
222
+ QUERY_CANCELED = '57014',
223
+ ADMIN_SHUTDOWN = '57P01',
224
+ CRASH_SHUTDOWN = '57P02',
225
+ CANNOT_CONNECT_NOW = '57P03',
226
+ DATABASE_DROPPED = '57P04',
227
+ IDLE_SESSION_TIMEOUT = '57P05',
228
+ SYSTEM_ERROR = '58000',
229
+ IO_ERROR = '58030',
230
+ UNDEFINED_FILE = '58P01',
231
+ DUPLICATE_FILE = '58P02',
232
+ SNAPSHOT_TOO_OLD = '72000',
233
+ CONFIG_FILE_ERROR = 'F0000',
234
+ LOCK_FILE_EXISTS = 'F0001',
235
+ FDW_ERROR = 'HV000',
236
+ FDW_COLUMN_NAME_NOT_FOUND = 'HV005',
237
+ FDW_DYNAMIC_PARAMETER_VALUE_NEEDED = 'HV002',
238
+ FDW_FUNCTION_SEQUENCE_ERROR = 'HV010',
239
+ FDW_INCONSISTENT_DESCRIPTOR_INFORMATION = 'HV021',
240
+ FDW_INVALID_ATTRIBUTE_VALUE = 'HV024',
241
+ FDW_INVALID_COLUMN_NAME = 'HV007',
242
+ FDW_INVALID_COLUMN_NUMBER = 'HV008',
243
+ FDW_INVALID_DATA_TYPE = 'HV004',
244
+ FDW_INVALID_DATA_TYPE_DESCRIPTORS = 'HV006',
245
+ FDW_INVALID_DESCRIPTOR_FIELD_IDENTIFIER = 'HV091',
246
+ FDW_INVALID_HANDLE = 'HV00B',
247
+ FDW_INVALID_OPTION_INDEX = 'HV00C',
248
+ FDW_INVALID_OPTION_NAME = 'HV00D',
249
+ FDW_INVALID_STRING_LENGTH_OR_BUFFER_LENGTH = 'HV090',
250
+ FDW_INVALID_STRING_FORMAT = 'HV00A',
251
+ FDW_INVALID_USE_OF_NULL_POINTER = 'HV009',
252
+ FDW_TOO_MANY_HANDLES = 'HV014',
253
+ FDW_OUT_OF_MEMORY = 'HV001',
254
+ FDW_NO_SCHEMAS = 'HV00P',
255
+ FDW_OPTION_NAME_NOT_FOUND = 'HV00J',
256
+ FDW_REPLY_HANDLE = 'HV00K',
257
+ FDW_SCHEMA_NOT_FOUND = 'HV00Q',
258
+ FDW_TABLE_NOT_FOUND = 'HV00R',
259
+ FDW_UNABLE_TO_CREATE_EXECUTION = 'HV00L',
260
+ FDW_UNABLE_TO_CREATE_REPLY = 'HV00M',
261
+ FDW_UNABLE_TO_ESTABLISH_CONNECTION = 'HV00N',
262
+ PLPGSQL_ERROR = 'P0000',
263
+ RAISE_EXCEPTION = 'P0001',
264
+ NO_DATA_FOUND = 'P0002',
265
+ TOO_MANY_ROWS = 'P0003',
266
+ ASSERT_FAILURE = 'P0004',
267
+ INTERNAL_ERROR = 'XX000',
268
+ DATA_CORRUPTED = 'XX001',
269
+ INDEX_CORRUPTED = 'XX002',
270
+ }
@@ -0,0 +1,18 @@
1
+ import { assertAssign } from './assertAssign';
2
+ import { assertIsString } from './assertIsString';
3
+
4
+ describe('assertAssign', () => {
5
+ it('should assert the input meets an assertion function and return if valid', () => {
6
+ expect(assertAssign('123', assertIsString)).toEqual('123');
7
+ });
8
+
9
+ it('should assert the input meets an assertion function and throw if not valid', () => {
10
+ expect(() => assertAssign(123, assertIsString)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
11
+ });
12
+
13
+ it('should allow custom error message', () => {
14
+ expect(() => assertAssign(123, assertIsString, 'custom error')).toThrowErrorMatchingInlineSnapshot(
15
+ `"custom error"`,
16
+ );
17
+ });
18
+ });
@@ -0,0 +1,22 @@
1
+ import { AssertionError } from 'assert';
2
+
3
+ /**
4
+ * Assert and return unknown value as known type
5
+ */
6
+ export function assertAssign<T>(
7
+ value: unknown,
8
+ assertionFunc: (val: unknown) => asserts val is T,
9
+ errorMessage?: string,
10
+ ): T {
11
+ try {
12
+ assertionFunc(value);
13
+ } catch (e) {
14
+ if (errorMessage) {
15
+ throw new AssertionError({ message: errorMessage });
16
+ } else {
17
+ throw e;
18
+ }
19
+ }
20
+
21
+ return value;
22
+ }
@@ -0,0 +1,31 @@
1
+ import { assertAssignWithDefaultUndefinedValue } from './assertAssignWithDefaultUndefinedValue';
2
+ import { assertIsObject } from './assertIsObject';
3
+ import { assertIsString } from './assertIsString';
4
+
5
+ describe('assertAssignWithDefaultUndefinedValue', () => {
6
+ it('should assert the input meets an assertion function and return if valid', () => {
7
+ expect(assertAssignWithDefaultUndefinedValue('123', assertIsString, 'xx')).toEqual('123');
8
+ });
9
+
10
+ it('should return default value if input is undefined', () => {
11
+ expect(assertAssignWithDefaultUndefinedValue(undefined, assertIsString, 'xx')).toEqual('xx');
12
+ });
13
+
14
+ it('should handle different types of type assertions', () => {
15
+ expect(assertAssignWithDefaultUndefinedValue(undefined, assertIsObject, { default: 'hello' })).toEqual({
16
+ default: 'hello',
17
+ });
18
+ });
19
+
20
+ it('should assert the input meets an assertion function and throw if not valid', () => {
21
+ expect(() => assertAssignWithDefaultUndefinedValue(123, assertIsString, 'xx')).toThrowErrorMatchingInlineSnapshot(
22
+ `"value is not a string"`,
23
+ );
24
+ });
25
+
26
+ it('should allow custom error message', () => {
27
+ expect(() =>
28
+ assertAssignWithDefaultUndefinedValue(123, assertIsString, 'xx', 'custom error'),
29
+ ).toThrowErrorMatchingInlineSnapshot(`"custom error"`);
30
+ });
31
+ });
@@ -0,0 +1,17 @@
1
+ import { assertAssign } from './assertAssign';
2
+
3
+ /**
4
+ * If value is undefined, return default value otherwise assert it meets assertion rule
5
+ */
6
+ export function assertAssignWithDefaultUndefinedValue<T>(
7
+ value: unknown,
8
+ assertionFunc: (val: unknown) => asserts val is T,
9
+ defaultValue: T,
10
+ errorMessage?: string,
11
+ ): T {
12
+ if (value === undefined || value === null) {
13
+ return defaultValue;
14
+ }
15
+
16
+ return assertAssign(value, assertionFunc, errorMessage);
17
+ }
@@ -0,0 +1,19 @@
1
+ import { assertIsDefined } from './assertIsDefined';
2
+
3
+ describe('assertIsDefined', () => {
4
+ it('should throw an assertion error if input is undefined', () => {
5
+ expect(() => assertIsDefined(undefined)).toThrowErrorMatchingInlineSnapshot(
6
+ `"Expected 'val' to be defined, but received undefined"`,
7
+ );
8
+ });
9
+
10
+ it('should throw an assertion error if input is null', () => {
11
+ expect(() => assertIsDefined(null)).toThrowErrorMatchingInlineSnapshot(
12
+ `"Expected 'val' to be defined, but received null"`,
13
+ );
14
+ });
15
+
16
+ it('should return "void" or undefined if valid', () => {
17
+ expect(assertIsDefined(123)).toBeUndefined();
18
+ });
19
+ });
@@ -0,0 +1,7 @@
1
+ import { AssertionError } from 'assert';
2
+
3
+ export function assertIsDefined<T>(value: T): asserts value is NonNullable<T> {
4
+ if (value === undefined || value === null) {
5
+ throw new AssertionError({ message: `Expected 'val' to be defined, but received ${value}` });
6
+ }
7
+ }
@@ -0,0 +1,23 @@
1
+ import { assertIsMapOfStringString } from './assertIsMapOfStringString';
2
+
3
+ describe('assertIsMapOfStringString', () => {
4
+ it('should not throw if input is an object with only string values', () => {
5
+ expect(assertIsMapOfStringString({ prop: 'aa' })).toBeUndefined();
6
+ });
7
+ it('should not throw if input is an empty object', () => {
8
+ expect(assertIsMapOfStringString({})).toBeUndefined();
9
+ });
10
+
11
+ it("should throw if object property isn't a string", () => {
12
+ expect(() => assertIsMapOfStringString({ prop: 123 })).toThrowErrorMatchingInlineSnapshot(
13
+ `"property prop is not type of string"`,
14
+ );
15
+ });
16
+
17
+ it('should throw if input is not an object', () => {
18
+ expect(() => assertIsMapOfStringString(123)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
19
+ expect(() => assertIsMapOfStringString(undefined)).toThrowErrorMatchingInlineSnapshot(
20
+ `"value cannot be null or undefined"`,
21
+ );
22
+ });
23
+ });
@@ -0,0 +1,29 @@
1
+ import { AssertionError } from 'assert';
2
+
3
+ export function assertIsMapOfStringString(value: unknown): asserts value is { [key: string]: string } {
4
+ if (!value) {
5
+ throw new AssertionError({
6
+ actual: value,
7
+ expected: 'not null',
8
+ message: 'value cannot be null or undefined',
9
+ });
10
+ }
11
+
12
+ if (typeof value !== 'object') {
13
+ throw new AssertionError({
14
+ actual: value,
15
+ expected: 'string',
16
+ message: 'value is not a string',
17
+ });
18
+ }
19
+
20
+ for (const [key, propValue] of Object.entries(value)) {
21
+ if (typeof propValue !== 'string') {
22
+ throw new AssertionError({
23
+ actual: typeof propValue,
24
+ expected: 'string',
25
+ message: `property ${key} is not type of string`,
26
+ });
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,25 @@
1
+ import { assertIsNotAnEmptyString } from './assertIsNotAnEmptyString';
2
+
3
+ describe('assertIsNotAnEmptyString', () => {
4
+ it('should throw if input value is not a string', () => {
5
+ expect(() => assertIsNotAnEmptyString(null)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
6
+ expect(() => assertIsNotAnEmptyString(undefined)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
7
+ expect(() => assertIsNotAnEmptyString(132)).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
8
+ expect(() => assertIsNotAnEmptyString({})).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
9
+ expect(() => assertIsNotAnEmptyString(['aa'])).toThrowErrorMatchingInlineSnapshot(`"value is not a string"`);
10
+ });
11
+
12
+ it('should throw if input is empty string', () => {
13
+ expect(() => assertIsNotAnEmptyString('')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
14
+ });
15
+
16
+ it('should throw if input is whitespace string', () => {
17
+ expect(() => assertIsNotAnEmptyString(' ')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
18
+ expect(() => assertIsNotAnEmptyString(' ')).toThrowErrorMatchingInlineSnapshot(`"string cannot be empty"`);
19
+ });
20
+
21
+ it('should not throw if string of length >= 1 is passed in', () => {
22
+ expect(assertIsNotAnEmptyString('1')).toBeUndefined();
23
+ expect(assertIsNotAnEmptyString('11111')).toBeUndefined();
24
+ });
25
+ });
@@ -0,0 +1,17 @@
1
+ import { AssertionError } from 'assert';
2
+
3
+ export function assertIsNotAnEmptyString(val: any): asserts val is string {
4
+ if (typeof val !== 'string') {
5
+ throw new AssertionError({
6
+ actual: typeof val,
7
+ expected: 'string',
8
+ message: 'value is not a string',
9
+ });
10
+ }
11
+
12
+ if (val.trim().length === 0) {
13
+ throw new AssertionError({
14
+ message: 'string cannot be empty',
15
+ });
16
+ }
17
+ }