@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.
- package/CHANGELOG.md +16 -0
- package/lib/AbstractRepository.d.ts +5 -3
- package/lib/PostgresErrorCodes.d.ts +268 -0
- package/lib/assertions/assertAssign.d.ts +4 -0
- package/lib/assertions/assertAssign.spec.d.ts +1 -0
- package/lib/assertions/assertAssignWithDefaultUndefinedValue.d.ts +4 -0
- package/lib/assertions/assertAssignWithDefaultUndefinedValue.spec.d.ts +1 -0
- package/lib/assertions/assertIsDefined.d.ts +1 -0
- package/lib/assertions/assertIsDefined.spec.d.ts +1 -0
- package/lib/assertions/assertIsMapOfStringString.d.ts +3 -0
- package/lib/assertions/assertIsMapOfStringString.spec.d.ts +1 -0
- package/lib/assertions/assertIsNotAnEmptyString.d.ts +1 -0
- package/lib/assertions/assertIsNotAnEmptyString.spec.d.ts +1 -0
- package/lib/assertions/assertIsObject.d.ts +1 -0
- package/lib/assertions/assertIsObject.spec.d.ts +1 -0
- package/lib/assertions/assertIsString.d.ts +1 -0
- package/lib/assertions/assertIsString.spec.d.ts +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +12369 -1364
- package/lib/index.js.map +4 -4
- package/package.json +10 -9
- package/src/AbstractRepository.ts +11 -7
- package/src/Migrator.ts +13 -9
- package/src/PostgresErrorCodes.ts +270 -0
- package/src/assertions/assertAssign.spec.ts +18 -0
- package/src/assertions/assertAssign.ts +22 -0
- package/src/assertions/assertAssignWithDefaultUndefinedValue.spec.ts +31 -0
- package/src/assertions/assertAssignWithDefaultUndefinedValue.ts +17 -0
- package/src/assertions/assertIsDefined.spec.ts +19 -0
- package/src/assertions/assertIsDefined.ts +7 -0
- package/src/assertions/assertIsMapOfStringString.spec.ts +23 -0
- package/src/assertions/assertIsMapOfStringString.ts +29 -0
- package/src/assertions/assertIsNotAnEmptyString.spec.ts +25 -0
- package/src/assertions/assertIsNotAnEmptyString.ts +17 -0
- package/src/assertions/assertIsObject.spec.ts +17 -0
- package/src/assertions/assertIsObject.ts +11 -0
- package/src/assertions/assertIsString.spec.ts +16 -0
- package/src/assertions/assertIsString.ts +11 -0
- package/src/index.ts +14 -0
- package/tsconfig.json +5 -1
- 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.
|
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": "
|
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.
|
22
|
-
"eslint": "8.
|
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.
|
26
|
+
"ts-jest": "28.0.7",
|
27
27
|
"ts-loader": "9.3.1",
|
28
|
-
"ts-node": "10.
|
28
|
+
"ts-node": "10.9.1",
|
29
29
|
"typescript": "4.7.4"
|
30
30
|
},
|
31
31
|
"dependencies": {
|
32
|
-
"@aws-sdk/client-secrets-manager": "
|
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": "
|
35
|
+
"pg": "8.7.3"
|
35
36
|
},
|
36
|
-
"gitHead": "
|
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
|
-
|
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 [
|
37
|
+
const [modelProp, columnName] = curr as [string, string];
|
35
38
|
prev[columnName] = modelProp;
|
36
39
|
return prev;
|
37
|
-
}, {} as { [key: string]:
|
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
|
137
|
+
const inputData: Record<string, unknown> = {};
|
135
138
|
|
136
139
|
for (const key of Object.keys(row)) {
|
137
|
-
|
140
|
+
const translatedKey = this.sqlColumnToModelProperty[key];
|
141
|
+
inputData[translatedKey] = row[key];
|
138
142
|
}
|
139
143
|
|
140
|
-
return
|
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
|
-
|
32
|
+
logger.info('Applying ' + migration);
|
29
33
|
|
30
34
|
if (result.rowCount !== undefined) {
|
31
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
92
|
+
logger.info('completed migration');
|
89
93
|
await this.releaseLock();
|
90
94
|
|
91
95
|
this.dispose();
|
92
96
|
} catch (e) {
|
93
|
-
|
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
|
-
|
108
|
+
logger.info('No pending migrations');
|
105
109
|
return;
|
106
110
|
}
|
107
111
|
|
108
|
-
|
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
|
-
|
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
|
+
}
|