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

Sign up to get free protection for your applications and to get access to all the features.
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
+ }