@squiz/db-lib 1.2.1-alpha.107 → 1.2.1-alpha.89

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -3,118 +3,6 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [1.2.1-alpha.107](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.107) (2022-09-21)
7
-
8
- **Note:** Version bump only for package @squiz/db-lib
9
-
10
-
11
-
12
-
13
-
14
- ## [1.2.1-alpha.106](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.106) (2022-09-02)
15
-
16
- **Note:** Version bump only for package @squiz/db-lib
17
-
18
-
19
-
20
-
21
-
22
- ## [1.2.1-alpha.105](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.105) (2022-08-26)
23
-
24
- **Note:** Version bump only for package @squiz/db-lib
25
-
26
-
27
-
28
-
29
-
30
- ## [1.2.1-alpha.104](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.104) (2022-08-23)
31
-
32
- **Note:** Version bump only for package @squiz/db-lib
33
-
34
-
35
-
36
-
37
-
38
- ## [1.2.1-alpha.103](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.103) (2022-08-23)
39
-
40
- **Note:** Version bump only for package @squiz/db-lib
41
-
42
-
43
-
44
-
45
-
46
- ## [1.2.1-alpha.102](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.102) (2022-08-23)
47
-
48
- **Note:** Version bump only for package @squiz/db-lib
49
-
50
-
51
-
52
-
53
-
54
- ## [1.2.1-alpha.101](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.101) (2022-08-19)
55
-
56
- **Note:** Version bump only for package @squiz/db-lib
57
-
58
-
59
-
60
-
61
-
62
- ## [1.2.1-alpha.100](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.100) (2022-08-18)
63
-
64
- **Note:** Version bump only for package @squiz/db-lib
65
-
66
-
67
-
68
-
69
-
70
- ## [1.2.1-alpha.99](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.99) (2022-08-18)
71
-
72
- **Note:** Version bump only for package @squiz/db-lib
73
-
74
-
75
-
76
-
77
-
78
- ## [1.2.1-alpha.98](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.98) (2022-08-17)
79
-
80
- **Note:** Version bump only for package @squiz/db-lib
81
-
82
-
83
-
84
-
85
-
86
- ## [1.2.1-alpha.97](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.97) (2022-08-17)
87
-
88
- **Note:** Version bump only for package @squiz/db-lib
89
-
90
-
91
-
92
-
93
-
94
- ## [1.2.1-alpha.96](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.96) (2022-08-12)
95
-
96
- **Note:** Version bump only for package @squiz/db-lib
97
-
98
-
99
-
100
-
101
-
102
- ## [1.2.1-alpha.95](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.90...v1.2.1-alpha.95) (2022-08-10)
103
-
104
- **Note:** Version bump only for package @squiz/db-lib
105
-
106
-
107
-
108
-
109
-
110
- ## [1.2.1-alpha.90](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.89...v1.2.1-alpha.90) (2022-08-05)
111
-
112
- **Note:** Version bump only for package @squiz/db-lib
113
-
114
-
115
-
116
-
117
-
118
6
  ## [1.2.1-alpha.89](https://gitlab.squiz.net/developer-experience/cmp/compare/v1.2.1-alpha.88...v1.2.1-alpha.89) (2022-08-05)
119
7
 
120
8
  **Note:** Version bump only for package @squiz/db-lib
package/package.json CHANGED
@@ -1,36 +1,37 @@
1
1
  {
2
2
  "name": "@squiz/db-lib",
3
- "version": "1.2.1-alpha.107",
3
+ "version": "1.2.1-alpha.89",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
7
+ "start": "node ./lib/index.js",
7
8
  "compile": "node build.js && npx tsc",
8
9
  "lint": "eslint ./src --ext .ts",
9
- "test": "jest -c jest.config.ts --passWithNoTests",
10
+ "test": "echo \"tested in integration\"",
11
+ "test:integration___": "jest -c jest.config.ts",
10
12
  "test:update-snapshots": "jest -c jest.config.ts --updateSnapshot",
11
13
  "clean": "rimraf \".tsbuildinfo\" \"./lib\""
12
14
  },
13
15
  "author": "",
14
16
  "license": "ISC",
15
17
  "devDependencies": {
16
- "@types/jest": "28.1.8",
18
+ "@types/jest": "28.1.6",
17
19
  "@types/node": "17.0.27",
18
20
  "@types/pg": "8.6.5",
19
- "esbuild": "0.15.6",
20
- "eslint": "8.22.0",
21
+ "esbuild": "0.14.49",
22
+ "eslint": "8.18.0",
21
23
  "fs-extra": "10.1.0",
22
24
  "jest": "28.1.3",
23
25
  "rimraf": "3.0.2",
24
- "ts-jest": "28.0.8",
26
+ "ts-jest": "28.0.5",
25
27
  "ts-loader": "9.3.1",
26
- "ts-node": "10.9.1",
28
+ "ts-node": "10.8.1",
27
29
  "typescript": "4.7.4"
28
30
  },
29
31
  "dependencies": {
30
- "@aws-sdk/client-secrets-manager": "3.121.0",
31
- "@squiz/dx-logger-lib": "^1.2.1-alpha.107",
32
+ "@aws-sdk/client-secrets-manager": "^3.121.0",
32
33
  "dotenv": "16.0.1",
33
- "pg": "8.7.3"
34
+ "pg": "^8.7.3"
34
35
  },
35
- "gitHead": "dc09e24c89b0f6d6afd23e958523cf9c9add89ec"
36
+ "gitHead": "d42ff3393c6e336ee05519872dbb58112ef045d1"
36
37
  }
@@ -14,46 +14,34 @@ export interface Writer<T> {
14
14
 
15
15
  export type Repository<T> = Reader<T> & Writer<T>;
16
16
 
17
- export type PageResult<T> = {
18
- items: T[];
19
- totalCount: number;
20
- pageSize: number;
21
- };
22
-
23
- export type SortDirection = 'desc' | 'asc';
24
- export const DEFAULT_PAGE_SIZE = 20;
25
-
26
- export abstract class AbstractRepository<T, ObjT extends T> implements Reader<T>, Writer<T> {
17
+ export abstract class AbstractRepository<T> implements Reader<T>, Writer<T> {
27
18
  protected tableName: string;
28
19
 
29
- /** object where the key is the model property name amd the value is sql column name */
30
20
  protected modelPropertyToSqlColumn: { [key in keyof T]: string };
31
- /** object where the key is the sql column name and the value is the model property name */
32
- protected sqlColumnToModelProperty: { [key: string]: string };
21
+ protected sqlColumnToModelProperty: { [key: string]: keyof T };
33
22
 
34
23
  constructor(
35
24
  protected repositories: Repositories,
36
25
  protected pool: Pool,
37
26
  tableName: string,
38
27
  mapping: { [key in keyof T]: string },
39
- protected classRef: { new (data?: Record<string, unknown>): ObjT },
28
+ protected classRef: { new (): T },
40
29
  ) {
41
30
  this.tableName = `"${tableName}"`;
42
31
 
43
32
  this.modelPropertyToSqlColumn = mapping;
44
-
45
33
  this.sqlColumnToModelProperty = Object.entries(mapping).reduce((prev, curr) => {
46
- const [modelProp, columnName] = curr as [string, string];
34
+ const [modelProp, columnName] = curr as [keyof T, string];
47
35
  prev[columnName] = modelProp;
48
36
  return prev;
49
- }, {} as { [key: string]: string });
37
+ }, {} as { [key: string]: keyof T });
50
38
  }
51
39
 
52
40
  protected async getConnection(): Promise<PoolClient> {
53
41
  return await this.pool.connect();
54
42
  }
55
43
 
56
- async create(value: ObjT, transactionClient: PoolClient | null = null): Promise<T> {
44
+ async create(value: Partial<T>, transactionClient: PoolClient | null = null): Promise<T> {
57
45
  const columns = Object.keys(value)
58
46
  .map((a) => `"${this.modelPropertyToSqlColumn[a as keyof T]}"`)
59
47
  .join(', ');
@@ -125,16 +113,16 @@ export abstract class AbstractRepository<T, ObjT extends T> implements Reader<T>
125
113
  return sql;
126
114
  }
127
115
 
128
- protected async executeQueryRaw(
116
+ protected async executeQuery(
129
117
  query: string,
130
118
  values: any[],
131
119
  transactionClient: PoolClient | null = null,
132
- ): Promise<any[]> {
120
+ ): Promise<T[]> {
133
121
  const client = transactionClient ?? (await this.getConnection());
134
122
  try {
135
123
  const result = await client.query(query, values);
136
124
 
137
- return result.rows;
125
+ return result.rows.map((a) => this.createAndHydrateModel(a));
138
126
  } finally {
139
127
  if (client && !transactionClient) {
140
128
  client.release();
@@ -142,24 +130,14 @@ export abstract class AbstractRepository<T, ObjT extends T> implements Reader<T>
142
130
  }
143
131
  }
144
132
 
145
- protected async executeQuery(
146
- query: string,
147
- values: any[],
148
- transactionClient: PoolClient | null = null,
149
- ): Promise<T[]> {
150
- const rows = await this.executeQueryRaw(query, values, transactionClient);
151
- return rows.map((a) => this.createAndHydrateModel(a));
152
- }
153
-
154
133
  protected createAndHydrateModel(row: any): T {
155
- const inputData: Record<string, unknown> = {};
134
+ const model = new this.classRef();
156
135
 
157
136
  for (const key of Object.keys(row)) {
158
- const translatedKey = this.sqlColumnToModelProperty[key];
159
- inputData[translatedKey] = row[key];
137
+ model[this.sqlColumnToModelProperty[key]] = row[key];
160
138
  }
161
139
 
162
- return new this.classRef(inputData);
140
+ return model;
163
141
  }
164
142
 
165
143
  async findOne(item: Partial<T>): Promise<T | undefined> {
@@ -194,61 +172,4 @@ export abstract class AbstractRepository<T, ObjT extends T> implements Reader<T>
194
172
 
195
173
  return result;
196
174
  }
197
-
198
- async getCount(item: Partial<T> | null = null): Promise<number> {
199
- let whereClause = '';
200
- if (item) {
201
- whereClause = `WHERE ${this.createWhereStringFromPartialModel(item)}`;
202
- }
203
- const result = await this.executeQueryRaw(
204
- `SELECT count(*)
205
- FROM ${this.tableName} ${whereClause}`,
206
- item ? Object.values(item) : [],
207
- );
208
- return parseInt(result[0].count);
209
- }
210
-
211
- async getPage(
212
- pageNumber: number,
213
- sortBy: (keyof T)[] = [],
214
- direction: SortDirection = 'asc',
215
- pageSize: number | null = null,
216
- item: Partial<T> | null = null,
217
- ): Promise<PageResult<T>> {
218
- if (pageSize === null) {
219
- pageSize = DEFAULT_PAGE_SIZE;
220
- }
221
- if (pageNumber <= 0) {
222
- throw new Error(`Page number value cannot be less than 1`);
223
- }
224
- if (pageSize <= 0) {
225
- throw new Error(`Page size value cannot be less than 1`);
226
- }
227
- let whereClause = '';
228
- if (item) {
229
- whereClause = `WHERE ${this.createWhereStringFromPartialModel(item)}`;
230
- }
231
-
232
- let orderByClause = '';
233
- if (sortBy.length) {
234
- orderByClause = `ORDER BY ${sortBy
235
- .map((a) => this.modelPropertyToSqlColumn[a as keyof T])
236
- .join(',')} ${direction}`;
237
- }
238
- const offset = (pageNumber - 1) * pageSize;
239
-
240
- const items = await this.executeQuery(
241
- `SELECT *
242
- FROM ${this.tableName} ${whereClause} ${orderByClause}
243
- OFFSET ${offset}
244
- LIMIT ${pageSize}`,
245
- item ? Object.values(item) : [],
246
- );
247
-
248
- return {
249
- items,
250
- totalCount: await this.getCount(item),
251
- pageSize,
252
- };
253
- }
254
175
  }
package/src/Migrator.ts CHANGED
@@ -1,15 +1,11 @@
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';
6
4
 
7
5
  // Be carful about changing this value. This needs to be consistent across deployments
8
6
  // this ID is used to signal other running instances that a migration is executing.
9
7
  const MIGRATION_ADVISORY_LOCK = 4569465;
10
8
 
11
- const logger = getLogger({ name: 'db-migrator', meta: { pid: process.pid, hostname: os.hostname() } });
12
-
13
9
  export class Migrator {
14
10
  constructor(protected migrationDir: string, protected migrationList: string[], protected pool: PoolClient) {}
15
11
 
@@ -29,15 +25,15 @@ export class Migrator {
29
25
  protected async applyMigration(migration: string, sql: string) {
30
26
  try {
31
27
  const result = await this.pool.query(sql);
32
- logger.info('Applying ' + migration);
28
+ console.log(process.pid, 'Applying ' + migration);
33
29
 
34
30
  if (result.rowCount !== undefined) {
35
- logger.info('affected rows', result.rowCount);
31
+ console.log(process.pid, 'affected rows', result.rowCount);
36
32
  }
37
33
 
38
34
  await this.pool.query('insert into __migrations__ (id) values ($1)', [migration]);
39
35
  } catch (e) {
40
- logger.info('error occurred running migration', migration, e);
36
+ console.log(process.pid, 'error occurred running migration', migration, e);
41
37
  throw e;
42
38
  }
43
39
  }
@@ -83,18 +79,18 @@ export class Migrator {
83
79
  const lockObtained = await this.tryToObtainLock();
84
80
 
85
81
  if (lockObtained === false) {
86
- logger.info('migration already running');
82
+ console.log(process.pid, 'migration already running');
87
83
  await sleep(500);
88
84
  return await this.migrate();
89
85
  }
90
86
 
91
87
  await this.runMigrations();
92
- logger.info('completed migration');
88
+ console.log(process.pid, 'completed migration');
93
89
  await this.releaseLock();
94
90
 
95
91
  this.dispose();
96
92
  } catch (e) {
97
- logger.info('migration failed releasing lock');
93
+ console.log(process.pid, 'migration failed releasing lock');
98
94
  await this.releaseLock();
99
95
  throw e;
100
96
  }
@@ -105,11 +101,11 @@ export class Migrator {
105
101
  const pending = await this.getPending(this.migrationList, appliedMigrations);
106
102
 
107
103
  if (pending.length === 0) {
108
- logger.info('No pending migrations');
104
+ console.log(process.pid, 'No pending migrations');
109
105
  return;
110
106
  }
111
107
 
112
- logger.info('Pending migrations:\n\t', pending.join('\n\t'));
108
+ console.log(process.pid, 'Pending migrations:\n\t', pending.join('\n\t'));
113
109
 
114
110
  for (const migration of pending) {
115
111
  await this.runMigration(migration);
@@ -125,7 +121,7 @@ export class Migrator {
125
121
 
126
122
  await this.pool.query('COMMIT');
127
123
  } catch (e) {
128
- logger.error('migration failed', migration, e);
124
+ console.log(process.pid, 'migration failed', migration, e);
129
125
  await this.pool.query('ROLLBACK');
130
126
  throw e;
131
127
  }
package/src/index.ts CHANGED
@@ -3,8 +3,3 @@ export * from './ConnectionManager';
3
3
  export * from './Migrator';
4
4
  export * from './Repositories';
5
5
  export * from './getConnectionInfo';
6
-
7
- // Postgres
8
- export * from './PostgresErrorCodes';
9
-
10
- export { Pool } from 'pg';
package/tsconfig.json CHANGED
@@ -7,11 +7,7 @@
7
7
  "emitDeclarationOnly": true,
8
8
  "rootDir": "./src"
9
9
  },
10
- "references": [
11
- {
12
- "path": "../dx-logger-lib"
13
- }
14
- ],
10
+ "references": [],
15
11
  "include": ["src/**/*.ts", "src/**/*.json"],
16
12
  "exclude": ["jest.config.ts"]
17
13
  }
@@ -1,53 +0,0 @@
1
- import { PoolClient, Pool } from 'pg';
2
- import { Repositories } from './Repositories';
3
- export interface Reader<T> {
4
- find(item: Partial<T>): Promise<T[]>;
5
- findOne(id: string | Partial<T>): Promise<T | undefined>;
6
- }
7
- export interface Writer<T> {
8
- create(value: Partial<T>): Promise<T>;
9
- update(where: Partial<T>, newValue: Partial<T>): Promise<T[]>;
10
- delete(where: Partial<T>): Promise<number>;
11
- }
12
- export declare type Repository<T> = Reader<T> & Writer<T>;
13
- export declare type PageResult<T> = {
14
- items: T[];
15
- totalCount: number;
16
- pageSize: number;
17
- };
18
- export declare type SortDirection = 'desc' | 'asc';
19
- export declare const DEFAULT_PAGE_SIZE = 20;
20
- export declare abstract class AbstractRepository<T, ObjT extends T> implements Reader<T>, Writer<T> {
21
- protected repositories: Repositories;
22
- protected pool: Pool;
23
- protected classRef: {
24
- new (data?: Record<string, unknown>): ObjT;
25
- };
26
- protected tableName: string;
27
- /** object where the key is the model property name amd the value is sql column name */
28
- protected modelPropertyToSqlColumn: {
29
- [key in keyof T]: string;
30
- };
31
- /** object where the key is the sql column name and the value is the model property name */
32
- protected sqlColumnToModelProperty: {
33
- [key: string]: string;
34
- };
35
- constructor(repositories: Repositories, pool: Pool, tableName: string, mapping: {
36
- [key in keyof T]: string;
37
- }, classRef: {
38
- new (data?: Record<string, unknown>): ObjT;
39
- });
40
- protected getConnection(): Promise<PoolClient>;
41
- create(value: ObjT, transactionClient?: PoolClient | null): Promise<T>;
42
- update(where: Partial<T>, newValue: Partial<T>, transactionClient?: PoolClient | null): Promise<T[]>;
43
- delete(where: Partial<T>, transactionClient?: PoolClient | null): Promise<number>;
44
- protected createWhereStringFromPartialModel(values: Partial<T>, initialIndex?: number): string;
45
- protected executeQueryRaw(query: string, values: any[], transactionClient?: PoolClient | null): Promise<any[]>;
46
- protected executeQuery(query: string, values: any[], transactionClient?: PoolClient | null): Promise<T[]>;
47
- protected createAndHydrateModel(row: any): T;
48
- findOne(item: Partial<T>): Promise<T | undefined>;
49
- find(item: Partial<T>): Promise<T[]>;
50
- findAll(): Promise<T[]>;
51
- getCount(item?: Partial<T> | null): Promise<number>;
52
- getPage(pageNumber: number, sortBy?: (keyof T)[], direction?: SortDirection, pageSize?: number | null, item?: Partial<T> | null): Promise<PageResult<T>>;
53
- }
@@ -1,25 +0,0 @@
1
- import { Pool } from 'pg';
2
- import { PoolClient } from 'pg';
3
- import { Repositories } from './Repositories';
4
- export interface DbConnection {
5
- user: string;
6
- password: string;
7
- host: string;
8
- port: number;
9
- database: string;
10
- }
11
- export interface ConnectionStringObj {
12
- connectionString: string;
13
- }
14
- export declare type TransactionClient = PoolClient;
15
- export declare class ConnectionManager<T extends Repositories> {
16
- protected applicationName: string;
17
- protected migrationDirectory: string;
18
- protected migrationList: string[];
19
- readonly pool: Pool;
20
- readonly repositories: T;
21
- constructor(applicationName: string, connection: string | DbConnection, migrationDirectory: string, migrationList: string[], repositoryCreator: (dbManager: ConnectionManager<T>) => T);
22
- applyMigrations(): Promise<void>;
23
- close(): Promise<void>;
24
- executeInTransaction<T>(func: (client: TransactionClient) => Promise<T>): Promise<T>;
25
- }
package/lib/Migrator.d.ts DELETED
@@ -1,18 +0,0 @@
1
- import { PoolClient } from 'pg';
2
- export declare class Migrator {
3
- protected migrationDir: string;
4
- protected migrationList: string[];
5
- protected pool: PoolClient;
6
- constructor(migrationDir: string, migrationList: string[], pool: PoolClient);
7
- protected ensureMigrationTableExists(): Promise<import("pg").QueryResult<any>>;
8
- protected getAppliedMigrations(): Promise<any[]>;
9
- protected applyMigration(migration: string, sql: string): Promise<void>;
10
- protected getPending(migrationsList: string[], appliedMigrations: string[]): Promise<string[]>;
11
- protected getSql(migration: string): Promise<string>;
12
- protected tryToObtainLock(): Promise<boolean>;
13
- protected releaseLock(): Promise<void>;
14
- migrate(): Promise<any>;
15
- protected runMigrations(): Promise<void>;
16
- protected runMigration(migration: string): Promise<void>;
17
- protected dispose(): void;
18
- }