@proteinjs/db 1.0.15 → 1.0.17

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 (103) hide show
  1. package/.eslintrc.js +20 -0
  2. package/.prettierignore +4 -0
  3. package/.prettierrc +8 -0
  4. package/CHANGELOG.md +16 -26
  5. package/dist/generated/index.js +1 -1
  6. package/dist/generated/index.js.map +1 -1
  7. package/dist/src/Columns.d.ts.map +1 -1
  8. package/dist/src/Columns.js +34 -17
  9. package/dist/src/Columns.js.map +1 -1
  10. package/dist/src/Db.d.ts.map +1 -1
  11. package/dist/src/Db.js +45 -18
  12. package/dist/src/Db.js.map +1 -1
  13. package/dist/src/MigrationRunner.d.ts.map +1 -1
  14. package/dist/src/MigrationRunner.js +15 -7
  15. package/dist/src/MigrationRunner.js.map +1 -1
  16. package/dist/src/QueryBuilderFactory.d.ts.map +1 -1
  17. package/dist/src/QueryBuilderFactory.js +5 -7
  18. package/dist/src/QueryBuilderFactory.js.map +1 -1
  19. package/dist/src/Record.d.ts.map +1 -1
  20. package/dist/src/Record.js +4 -2
  21. package/dist/src/Record.js.map +1 -1
  22. package/dist/src/StatementConfigFactory.d.ts.map +1 -1
  23. package/dist/src/StatementConfigFactory.js +3 -2
  24. package/dist/src/StatementConfigFactory.js.map +1 -1
  25. package/dist/src/Table.d.ts.map +1 -1
  26. package/dist/src/Table.js +6 -3
  27. package/dist/src/Table.js.map +1 -1
  28. package/dist/src/auth/TableAuth.d.ts.map +1 -1
  29. package/dist/src/auth/TableAuth.js +16 -11
  30. package/dist/src/auth/TableAuth.js.map +1 -1
  31. package/dist/src/auth/TableServiceAuth.d.ts.map +1 -1
  32. package/dist/src/auth/TableServiceAuth.js +12 -6
  33. package/dist/src/auth/TableServiceAuth.js.map +1 -1
  34. package/dist/src/reference/Reference.d.ts.map +1 -1
  35. package/dist/src/reference/Reference.js.map +1 -1
  36. package/dist/src/reference/ReferenceArray.d.ts.map +1 -1
  37. package/dist/src/reference/ReferenceArray.js.map +1 -1
  38. package/dist/src/schema/SchemaMetadata.d.ts.map +1 -1
  39. package/dist/src/schema/SchemaMetadata.js +18 -15
  40. package/dist/src/schema/SchemaMetadata.js.map +1 -1
  41. package/dist/src/schema/SchemaOperations.d.ts.map +1 -1
  42. package/dist/src/schema/TableManager.d.ts.map +1 -1
  43. package/dist/src/schema/TableManager.js +24 -9
  44. package/dist/src/schema/TableManager.js.map +1 -1
  45. package/dist/src/serializers/GraphSerializer.d.ts.map +1 -1
  46. package/dist/src/serializers/QueryBuilderSerializer.d.ts.map +1 -1
  47. package/dist/src/serializers/ReferenceArraySerializer.d.ts.map +1 -1
  48. package/dist/src/serializers/ReferenceArraySerializer.js.map +1 -1
  49. package/dist/src/serializers/ReferenceSerializer.d.ts.map +1 -1
  50. package/dist/src/serializers/TableSerializer.d.ts.map +1 -1
  51. package/dist/src/services/DbService.d.ts.map +1 -1
  52. package/dist/src/source/SourceRecord.d.ts.map +1 -1
  53. package/dist/src/source/SourceRecord.js +7 -3
  54. package/dist/src/source/SourceRecord.js.map +1 -1
  55. package/dist/src/source/SourceRecordLoader.d.ts.map +1 -1
  56. package/dist/src/source/SourceRecordLoader.js +11 -4
  57. package/dist/src/source/SourceRecordLoader.js.map +1 -1
  58. package/dist/src/source/SourceRecordRepo.d.ts.map +1 -1
  59. package/dist/src/source/SourceRecordRepo.js.map +1 -1
  60. package/dist/src/tables/MigrationTable.d.ts.map +1 -1
  61. package/dist/src/tables/MigrationTable.js.map +1 -1
  62. package/dist/src/tables/tables.d.ts.map +1 -1
  63. package/dist/src/tables/tables.js.map +1 -1
  64. package/dist/test/CrudTests.d.ts.map +1 -1
  65. package/dist/test/CrudTests.js +13 -4
  66. package/dist/test/CrudTests.js.map +1 -1
  67. package/dist/test/TableManagerTests.d.ts.map +1 -1
  68. package/dist/test/TableManagerTests.js +17 -9
  69. package/dist/test/TableManagerTests.js.map +1 -1
  70. package/generated/index.ts +15 -18
  71. package/index.ts +1 -1
  72. package/jest.config.js +8 -17
  73. package/package.json +48 -43
  74. package/src/Columns.ts +284 -241
  75. package/src/Db.ts +188 -147
  76. package/src/MigrationRunner.ts +24 -10
  77. package/src/QueryBuilderFactory.ts +6 -9
  78. package/src/Record.ts +35 -29
  79. package/src/StatementConfigFactory.ts +14 -16
  80. package/src/Table.ts +91 -88
  81. package/src/auth/TableAuth.ts +37 -27
  82. package/src/auth/TableServiceAuth.ts +11 -9
  83. package/src/opt/moment.ts +1 -1
  84. package/src/reference/Reference.ts +7 -7
  85. package/src/reference/ReferenceArray.ts +8 -8
  86. package/src/schema/SchemaMetadata.ts +84 -64
  87. package/src/schema/SchemaOperations.ts +2 -2
  88. package/src/schema/TableManager.ts +178 -168
  89. package/src/serializers/GraphSerializer.ts +4 -4
  90. package/src/serializers/QueryBuilderSerializer.ts +11 -11
  91. package/src/serializers/ReferenceArraySerializer.ts +12 -8
  92. package/src/serializers/ReferenceSerializer.ts +7 -7
  93. package/src/serializers/TableSerializer.ts +5 -5
  94. package/src/services/DbService.ts +3 -3
  95. package/src/services/MigrationRunnerService.ts +1 -1
  96. package/src/source/SourceRecord.ts +43 -24
  97. package/src/source/SourceRecordLoader.ts +29 -18
  98. package/src/source/SourceRecordRepo.ts +3 -3
  99. package/src/tables/MigrationTable.ts +5 -5
  100. package/src/tables/tables.ts +1 -1
  101. package/test/CrudTests.ts +43 -30
  102. package/test/TableManagerTests.ts +329 -154
  103. package/tsconfig.json +18 -22
package/src/Db.ts CHANGED
@@ -12,191 +12,232 @@ import { TableManager } from './schema/TableManager';
12
12
  import { TableAuth } from './auth/TableAuth';
13
13
  import { TableServiceAuth } from './auth/TableServiceAuth';
14
14
 
15
- export const getDb = <R extends Record = Record>() => typeof self === 'undefined' ? new Db<R>() : getDbService() as Db<R>;
15
+ export const getDb = <R extends Record = Record>() =>
16
+ typeof self === 'undefined' ? new Db<R>() : (getDbService() as Db<R>);
16
17
  export const getSystemDb = <R extends Record = Record>() => new Db<R>(undefined, undefined, true);
17
18
 
18
- export type DbDriverStatementConfig = ParameterizationConfig & { prefixTablesWithDb?: boolean }
19
+ export type DbDriverStatementConfig = ParameterizationConfig & { prefixTablesWithDb?: boolean };
19
20
 
20
21
  export interface DefaultDbDriverFactory extends Loadable {
21
- getDbDriver(): DbDriver;
22
+ getDbDriver(): DbDriver;
22
23
  }
23
24
 
24
25
  export interface DbDriver {
25
- getDbName(): string;
26
- createDbIfNotExists(): Promise<void>;
27
- start?(): Promise<void>;
28
- stop?(): Promise<void>;
29
- getTableManager(): TableManager;
30
- runQuery(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<SerializedRecord[]>;
31
- runDml(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<number>; // returns the number of affected rows
26
+ getDbName(): string;
27
+ createDbIfNotExists(): Promise<void>;
28
+ start?(): Promise<void>;
29
+ stop?(): Promise<void>;
30
+ getTableManager(): TableManager;
31
+ runQuery(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<SerializedRecord[]>;
32
+ runDml(generateStatement: (config: DbDriverStatementConfig) => Statement): Promise<number>; // returns the number of affected rows
32
33
  }
33
34
 
34
35
  export class Db<R extends Record = Record> implements DbService<R> {
35
- private static defaultDbDriver: DbDriver;
36
- private dbDriver: DbDriver;
37
- private logger = new Logger(this.constructor.name);
38
- private statementConfigFactory: StatementConfigFactory;
39
- private auth = new TableAuth();
40
- public serviceMetadata: Service['serviceMetadata'] = {
41
- auth: {
42
- canAccess: (methodName, args) => new TableServiceAuth().canAccess(methodName, args),
43
- },
44
- };
45
-
46
- constructor(
47
- dbDriver?: DbDriver,
48
- getTable?: (tableName: string) => Table<any>,
49
- private ignoreAuth?: boolean
50
- ) {
51
- this.dbDriver = dbDriver ? dbDriver : this.getDefaultDbDriver();
52
- this.statementConfigFactory = new StatementConfigFactory(this.dbDriver.getDbName(), getTable);
36
+ private static defaultDbDriver: DbDriver;
37
+ private dbDriver: DbDriver;
38
+ private logger = new Logger(this.constructor.name);
39
+ private statementConfigFactory: StatementConfigFactory;
40
+ private auth = new TableAuth();
41
+ public serviceMetadata: Service['serviceMetadata'] = {
42
+ auth: {
43
+ canAccess: (methodName, args) => new TableServiceAuth().canAccess(methodName, args),
44
+ },
45
+ };
46
+
47
+ constructor(
48
+ dbDriver?: DbDriver,
49
+ getTable?: (tableName: string) => Table<any>,
50
+ private ignoreAuth?: boolean
51
+ ) {
52
+ this.dbDriver = dbDriver ? dbDriver : this.getDefaultDbDriver();
53
+ this.statementConfigFactory = new StatementConfigFactory(this.dbDriver.getDbName(), getTable);
54
+ }
55
+
56
+ private getDefaultDbDriver(): DbDriver {
57
+ if (!Db.defaultDbDriver) {
58
+ const defaultDbDriverFactory = SourceRepository.get().object<DefaultDbDriverFactory>(
59
+ '@proteinjs/db/DefaultDbDriverFactory'
60
+ );
61
+ if (!defaultDbDriverFactory) {
62
+ throw new Error(
63
+ `Unable to find a @proteinjs/db/DefaultDbDriverFactory implementation. Either implement DefaultDbDriverFactory or pass in a db driver when instantiating Db.`
64
+ );
65
+ }
66
+
67
+ Db.defaultDbDriver = defaultDbDriverFactory.getDbDriver();
53
68
  }
54
69
 
55
- private getDefaultDbDriver(): DbDriver {
56
- if (!Db.defaultDbDriver) {
57
- const defaultDbDriverFactory = SourceRepository.get().object<DefaultDbDriverFactory>('@proteinjs/db/DefaultDbDriverFactory');
58
- if (!defaultDbDriverFactory)
59
- throw new Error(`Unable to find a @proteinjs/db/DefaultDbDriverFactory implementation. Either implement DefaultDbDriverFactory or pass in a db driver when instantiating Db.`);
70
+ return Db.defaultDbDriver;
71
+ }
60
72
 
61
- Db.defaultDbDriver = defaultDbDriverFactory.getDbDriver();
62
- }
73
+ async init(): Promise<void> {
74
+ await this.dbDriver.createDbIfNotExists();
75
+ await this.dbDriver.getTableManager().loadTables();
76
+ await new SourceRecordLoader().load();
77
+ }
63
78
 
64
- return Db.defaultDbDriver;
65
- }
79
+ async tableExists<T extends R>(table: Table<T>): Promise<boolean> {
80
+ return await this.dbDriver.getTableManager().tableExists(table);
81
+ }
66
82
 
67
- async init(): Promise<void> {
68
- await this.dbDriver.createDbIfNotExists();
69
- await this.dbDriver.getTableManager().loadTables();
70
- await new SourceRecordLoader().load();
71
- }
83
+ async get<T extends R>(table: Table<T>, query: Query<T>): Promise<T> {
84
+ return (await this.query(table, query))[0];
85
+ }
72
86
 
73
- async tableExists<T extends R>(table: Table<T>): Promise<boolean> {
74
- return await this.dbDriver.getTableManager().tableExists(table);
87
+ async insert<T extends R>(table: Table<T>, record: Omit<T, keyof R>): Promise<T> {
88
+ if (!this.ignoreAuth) {
89
+ this.auth.canInsert(table);
75
90
  }
76
91
 
77
- async get<T extends R>(table: Table<T>, query: Query<T>): Promise<T> {
78
- return (await this.query(table, query))[0];
92
+ const recordCopy = Object.assign({}, record);
93
+ await this.addDefaultFieldValues(table, recordCopy);
94
+ const recordSearializer = new RecordSerializer(table);
95
+ const serializedRecord = await recordSearializer.serialize(recordCopy);
96
+ const generateInsert = (config: DbDriverStatementConfig) =>
97
+ new StatementFactory<T>().insert(
98
+ table.name,
99
+ serializedRecord as Partial<T>,
100
+ this.statementConfigFactory.getStatementConfig(config)
101
+ );
102
+ await this.dbDriver.runDml(generateInsert);
103
+ return recordCopy as T;
104
+ }
105
+
106
+ private async addDefaultFieldValues<T extends R>(table: Table<T>, record: any) {
107
+ for (const columnPropertyName in table.columns) {
108
+ const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
109
+ if (column.options?.defaultValue && typeof record[columnPropertyName] === 'undefined') {
110
+ record[columnPropertyName] = await column.options.defaultValue(record);
111
+ }
79
112
  }
113
+ }
80
114
 
81
- async insert<T extends R>(table: Table<T>, record: Omit<T, keyof R>): Promise<T> {
82
- if (!this.ignoreAuth)
83
- this.auth.canInsert(table);
84
-
85
- const recordCopy = Object.assign({}, record);
86
- await this.addDefaultFieldValues(table, recordCopy);
87
- const recordSearializer = new RecordSerializer(table);
88
- const serializedRecord = await recordSearializer.serialize(recordCopy);
89
- const generateInsert = (config: DbDriverStatementConfig) => new StatementFactory<T>().insert(table.name, serializedRecord as Partial<T>, this.statementConfigFactory.getStatementConfig(config));
90
- await this.dbDriver.runDml(generateInsert);
91
- return recordCopy as T;
115
+ async update<T extends R>(table: Table<T>, record: Partial<T>, query?: Query<T>): Promise<number> {
116
+ if (!this.ignoreAuth) {
117
+ this.auth.canUpdate(table);
92
118
  }
93
119
 
94
- private async addDefaultFieldValues<T extends R>(table: Table<T>, record: any) {
95
- for (let columnPropertyName in table.columns) {
96
- const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
97
- if (column.options?.defaultValue && typeof record[columnPropertyName] === 'undefined')
98
- record[columnPropertyName] = await column.options.defaultValue(record);
99
- }
120
+ if (!query && !record.id) {
121
+ throw new Error(`Update must be called with either a QueryBuilder or a record with an id property`);
100
122
  }
101
123
 
102
- async update<T extends R>(table: Table<T>, record: Partial<T>, query?: Query<T>): Promise<number> {
103
- if (!this.ignoreAuth)
104
- this.auth.canUpdate(table);
105
-
106
- if (!query && !record.id)
107
- throw new Error(`Update must be called with either a QueryBuilder or a record with an id property`);
108
-
109
- const recordCopy = Object.assign({}, record);
110
- await this.addUpdateFieldValues(table, recordCopy);
111
- const recordSearializer = new RecordSerializer<T>(table);
112
- const serializedRecord = await recordSearializer.serialize(recordCopy);
113
- const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
114
- if (!query)
115
- qb.condition({ field: 'id', operator: '=', value: recordCopy.id as T[keyof T] });
116
-
117
- delete serializedRecord['id'];
118
- const generateUpdate = (config: DbDriverStatementConfig) => new StatementFactory<T>().update(table.name, serializedRecord as Partial<T>, qb, this.statementConfigFactory.getStatementConfig(config));
119
- return await this.dbDriver.runDml(generateUpdate);
124
+ const recordCopy = Object.assign({}, record);
125
+ await this.addUpdateFieldValues(table, recordCopy);
126
+ const recordSearializer = new RecordSerializer<T>(table);
127
+ const serializedRecord = await recordSearializer.serialize(recordCopy);
128
+ const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
129
+ if (!query) {
130
+ qb.condition({ field: 'id', operator: '=', value: recordCopy.id as T[keyof T] });
120
131
  }
121
132
 
122
- private async addUpdateFieldValues<T extends R>(table: Table<T>, record: any) {
123
- for (let columnPropertyName in table.columns) {
124
- const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
125
- if (column.options?.updateValue)
126
- record[columnPropertyName] = await column.options.updateValue(record);
127
- }
133
+ delete serializedRecord['id'];
134
+ const generateUpdate = (config: DbDriverStatementConfig) =>
135
+ new StatementFactory<T>().update(
136
+ table.name,
137
+ serializedRecord as Partial<T>,
138
+ qb,
139
+ this.statementConfigFactory.getStatementConfig(config)
140
+ );
141
+ return await this.dbDriver.runDml(generateUpdate);
142
+ }
143
+
144
+ private async addUpdateFieldValues<T extends R>(table: Table<T>, record: any) {
145
+ for (const columnPropertyName in table.columns) {
146
+ const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
147
+ if (column.options?.updateValue) {
148
+ record[columnPropertyName] = await column.options.updateValue(record);
149
+ }
128
150
  }
151
+ }
129
152
 
130
- async delete<T extends R>(table: Table<T>, query: Query<T>): Promise<number> {
131
- if (!this.ignoreAuth)
132
- this.auth.canDelete(table);
133
-
134
- const recordsToDelete = await this.query(table, query);
135
- if (recordsToDelete.length == 0)
136
- return 0;
137
-
138
- const recordsToDeleteIds = recordsToDelete.map(record => record.id);
139
- const deleteQb = new QueryBuilderFactory().getQueryBuilder(table);
140
- deleteQb.condition({ field: 'id', operator: 'IN', value: recordsToDeleteIds as T[keyof T][] });
141
- const generateDelete = (config: DbDriverStatementConfig) => new StatementFactory<T>().delete(table.name, deleteQb, this.statementConfigFactory.getStatementConfig(config));
142
- await this.beforeDelete(table, recordsToDelete);
143
- const deletedRowCount = await this.dbDriver.runDml(generateDelete);
144
- await this.cascadeDeletions(table, recordsToDeleteIds);
145
- return deletedRowCount;
153
+ async delete<T extends R>(table: Table<T>, query: Query<T>): Promise<number> {
154
+ if (!this.ignoreAuth) {
155
+ this.auth.canDelete(table);
146
156
  }
147
157
 
148
- private async beforeDelete(table: Table<any>, recordsToDelete: Record[]) {
149
- for (let columnPropertyName in table.columns) {
150
- const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
151
- if (typeof column.beforeDelete !== 'undefined')
152
- await column.beforeDelete(table, columnPropertyName, recordsToDelete);
153
- }
158
+ const recordsToDelete = await this.query(table, query);
159
+ if (recordsToDelete.length == 0) {
160
+ return 0;
154
161
  }
155
162
 
156
- private async cascadeDeletions(table: Table<any>, deletedRecordIds: string[]) {
157
- if (table.cascadeDeleteReferences().length < 1)
158
- return;
159
-
160
- for (let cascadeDeleteReference of table.cascadeDeleteReferences()) {
161
- const referenceTable = tableByName(cascadeDeleteReference.table);
162
- const referenceColumnPropertyName = getColumnPropertyName(referenceTable, cascadeDeleteReference.referenceColumn);
163
- this.logger.info(`Executing cascade delete for table: ${table.name}, referenceTable: ${referenceTable.name}, referenceColumnPropertyName: ${referenceColumnPropertyName}, deletedRecordIds: ${JSON.stringify(deletedRecordIds)}`);
164
- const cascadeDeleteQb = new QueryBuilderFactory().getQueryBuilder(referenceTable);
165
- cascadeDeleteQb.condition({ field: referenceColumnPropertyName as string, operator: 'IN', value: deletedRecordIds });
166
- const deleteCount = await this.delete(referenceTable, cascadeDeleteQb);
167
- this.logger.info(`Cascade deleted ${deleteCount} record${deleteCount == 1 ? '' : 's'}`);
168
- }
163
+ const recordsToDeleteIds = recordsToDelete.map((record) => record.id);
164
+ const deleteQb = new QueryBuilderFactory().getQueryBuilder(table);
165
+ deleteQb.condition({ field: 'id', operator: 'IN', value: recordsToDeleteIds as T[keyof T][] });
166
+ const generateDelete = (config: DbDriverStatementConfig) =>
167
+ new StatementFactory<T>().delete(table.name, deleteQb, this.statementConfigFactory.getStatementConfig(config));
168
+ await this.beforeDelete(table, recordsToDelete);
169
+ const deletedRowCount = await this.dbDriver.runDml(generateDelete);
170
+ await this.cascadeDeletions(table, recordsToDeleteIds);
171
+ return deletedRowCount;
172
+ }
173
+
174
+ private async beforeDelete(table: Table<any>, recordsToDelete: Record[]) {
175
+ for (const columnPropertyName in table.columns) {
176
+ const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
177
+ if (typeof column.beforeDelete !== 'undefined') {
178
+ await column.beforeDelete(table, columnPropertyName, recordsToDelete);
179
+ }
169
180
  }
181
+ }
170
182
 
171
- async query<T extends R>(table: Table<T>, query: Query<T>): Promise<T[]> {
172
- if (!this.ignoreAuth)
173
- this.auth.canQuery(table);
183
+ private async cascadeDeletions(table: Table<any>, deletedRecordIds: string[]) {
184
+ if (table.cascadeDeleteReferences().length < 1) {
185
+ return;
186
+ }
174
187
 
175
- const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
176
- this.addColumnQueries(table, qb);
177
- const generateQuery = (config: DbDriverStatementConfig) => qb.toSql(this.statementConfigFactory.getStatementConfig(config));
178
- const serializedRecords = await this.dbDriver.runQuery(generateQuery);
179
- const recordSearializer = new RecordSerializer(table);
180
- return await Promise.all(serializedRecords.map(async (serializedRecord) => recordSearializer.deserialize(serializedRecord)));
188
+ for (const cascadeDeleteReference of table.cascadeDeleteReferences()) {
189
+ const referenceTable = tableByName(cascadeDeleteReference.table);
190
+ const referenceColumnPropertyName = getColumnPropertyName(referenceTable, cascadeDeleteReference.referenceColumn);
191
+ this.logger.info(
192
+ `Executing cascade delete for table: ${table.name}, referenceTable: ${referenceTable.name}, referenceColumnPropertyName: ${referenceColumnPropertyName}, deletedRecordIds: ${JSON.stringify(deletedRecordIds)}`
193
+ );
194
+ const cascadeDeleteQb = new QueryBuilderFactory().getQueryBuilder(referenceTable);
195
+ cascadeDeleteQb.condition({
196
+ field: referenceColumnPropertyName as string,
197
+ operator: 'IN',
198
+ value: deletedRecordIds,
199
+ });
200
+ const deleteCount = await this.delete(referenceTable, cascadeDeleteQb);
201
+ this.logger.info(`Cascade deleted ${deleteCount} record${deleteCount == 1 ? '' : 's'}`);
181
202
  }
203
+ }
182
204
 
183
- async getRowCount<T extends R>(table: Table<T>, query?: Query<T>): Promise<number> {
184
- if (!this.ignoreAuth)
185
- this.auth.canQuery(table);
205
+ async query<T extends R>(table: Table<T>, query: Query<T>): Promise<T[]> {
206
+ if (!this.ignoreAuth) {
207
+ this.auth.canQuery(table);
208
+ }
186
209
 
187
- const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
188
- qb.aggregate({ function: 'COUNT', resultProp: 'count' });
189
- this.addColumnQueries(table, qb);
190
- const generateQuery = (config: DbDriverStatementConfig) => qb.toSql(this.statementConfigFactory.getStatementConfig(config));
191
- const result = await this.dbDriver.runQuery(generateQuery);
192
- return result[0]['count'];
210
+ const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
211
+ this.addColumnQueries(table, qb);
212
+ const generateQuery = (config: DbDriverStatementConfig) =>
213
+ qb.toSql(this.statementConfigFactory.getStatementConfig(config));
214
+ const serializedRecords = await this.dbDriver.runQuery(generateQuery);
215
+ const recordSearializer = new RecordSerializer(table);
216
+ return await Promise.all(
217
+ serializedRecords.map(async (serializedRecord) => recordSearializer.deserialize(serializedRecord))
218
+ );
219
+ }
220
+
221
+ async getRowCount<T extends R>(table: Table<T>, query?: Query<T>): Promise<number> {
222
+ if (!this.ignoreAuth) {
223
+ this.auth.canQuery(table);
193
224
  }
194
225
 
195
- private async addColumnQueries<T extends R>(table: Table<T>, qb: QueryBuilder<T>) {
196
- for (let columnPropertyName in table.columns) {
197
- const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
198
- if (column.options?.addToQuery)
199
- column.options.addToQuery(qb);
200
- }
226
+ const qb = new QueryBuilderFactory().getQueryBuilder(table, query);
227
+ qb.aggregate({ function: 'COUNT', resultProp: 'count' });
228
+ this.addColumnQueries(table, qb);
229
+ const generateQuery = (config: DbDriverStatementConfig) =>
230
+ qb.toSql(this.statementConfigFactory.getStatementConfig(config));
231
+ const result = await this.dbDriver.runQuery(generateQuery);
232
+ return result[0]['count'];
233
+ }
234
+
235
+ private async addColumnQueries<T extends R>(table: Table<T>, qb: QueryBuilder<T>) {
236
+ for (const columnPropertyName in table.columns) {
237
+ const column = (table.columns as any)[columnPropertyName] as Column<any, any>;
238
+ if (column.options?.addToQuery) {
239
+ column.options.addToQuery(qb);
240
+ }
201
241
  }
202
- }
242
+ }
243
+ }
@@ -7,7 +7,8 @@ import { Migration, MigrationTable } from './tables/MigrationTable';
7
7
  import { Service } from '@proteinjs/service';
8
8
  import { Logger } from '@proteinjs/util';
9
9
 
10
- export const getMigrationRunner = () => typeof self === 'undefined' ? new MigrationRunner() : getMigrationRunnerService() as MigrationRunner;
10
+ export const getMigrationRunner = () =>
11
+ typeof self === 'undefined' ? new MigrationRunner() : (getMigrationRunnerService() as MigrationRunner);
11
12
 
12
13
  export class MigrationRunner implements MigrationRunnerService {
13
14
  private logger = new Logger(this.constructor.name);
@@ -18,8 +19,9 @@ export class MigrationRunner implements MigrationRunnerService {
18
19
  async runMigration(id: string): Promise<void> {
19
20
  const migrationTable: Table<Migration> = new MigrationTable();
20
21
  const migration = new SourceRecordRepo().getSourceRecord<Migration>(migrationTable.name, id);
21
- if (!migration)
22
+ if (!migration) {
22
23
  throw new Error(`Unable to find migration source record for id: ${id}`);
24
+ }
23
25
 
24
26
  const db = getDb();
25
27
  migration.status = 'running';
@@ -38,12 +40,14 @@ export class MigrationRunner implements MigrationRunnerService {
38
40
  }
39
41
  migration.duration = this.duration(migration.startTime, migration.endTime);
40
42
  await db.update(migrationTable, migration);
41
- this.logger.info(`[${migration.status}] (${migration.duration}) Finished running migration (${migration.id}) ${migration.description}`);
43
+ this.logger.info(
44
+ `[${migration.status}] (${migration.duration}) Finished running migration (${migration.id}) ${migration.description}`
45
+ );
42
46
  }
43
47
 
44
48
  private duration(start: Moment, end: Moment): string {
45
49
  const duration = moment.duration(end.diff(start));
46
- let parts: string[] = [];
50
+ const parts: string[] = [];
47
51
 
48
52
  const days = duration.days();
49
53
  const hours = duration.hours();
@@ -51,12 +55,22 @@ export class MigrationRunner implements MigrationRunnerService {
51
55
  const seconds = duration.seconds();
52
56
  const milliseconds = duration.milliseconds();
53
57
 
54
- if (days > 0) parts.push(`${days} day${days > 1 ? 's' : ''}`);
55
- if (hours > 0) parts.push(`${hours} hour${hours > 1 ? 's' : ''}`);
56
- if (minutes > 0) parts.push(`${minutes} min${minutes > 1 ? 's' : ''}`);
57
- if (seconds > 0) parts.push(`${seconds} sec${seconds > 1 ? 's' : ''}`);
58
- if (days == 0 && hours == 0 && minutes == 0 && seconds == 0) parts.push(`${milliseconds} ms`);
58
+ if (days > 0) {
59
+ parts.push(`${days} day${days > 1 ? 's' : ''}`);
60
+ }
61
+ if (hours > 0) {
62
+ parts.push(`${hours} hour${hours > 1 ? 's' : ''}`);
63
+ }
64
+ if (minutes > 0) {
65
+ parts.push(`${minutes} min${minutes > 1 ? 's' : ''}`);
66
+ }
67
+ if (seconds > 0) {
68
+ parts.push(`${seconds} sec${seconds > 1 ? 's' : ''}`);
69
+ }
70
+ if (days == 0 && hours == 0 && minutes == 0 && seconds == 0) {
71
+ parts.push(`${milliseconds} ms`);
72
+ }
59
73
 
60
74
  return parts.join(' ');
61
75
  }
62
- }
76
+ }
@@ -6,14 +6,11 @@ import { QueryBuilder } from '@proteinjs/db-query';
6
6
 
7
7
  export class QueryBuilderFactory {
8
8
  getQueryBuilder<T extends Record>(table: Table<T>, query?: Query<T>): QueryBuilder<T> {
9
- const qb = query ?
10
- isInstanceOf(query, QueryBuilder) ?
11
- query as QueryBuilder<T>
12
- :
13
- QueryBuilder.fromObject<T>(query as T, table.name)
14
- :
15
- new QueryBuilder<T>(table.name)
16
- ;
9
+ const qb = query
10
+ ? isInstanceOf(query, QueryBuilder)
11
+ ? (query as QueryBuilder<T>)
12
+ : QueryBuilder.fromObject<T>(query as T, table.name)
13
+ : new QueryBuilder<T>(table.name);
17
14
  return qb;
18
15
  }
19
- }
16
+ }
package/src/Record.ts CHANGED
@@ -3,50 +3,53 @@ import { Column, Table, Columns } from './Table';
3
3
  import { moment } from './opt/moment';
4
4
 
5
5
  export interface Record {
6
- id: string;
7
- created: moment.Moment;
8
- updated: moment.Moment;
6
+ id: string;
7
+ created: moment.Moment;
8
+ updated: moment.Moment;
9
9
  }
10
10
 
11
11
  const recordColumns: Columns<Record> = {
12
12
  id: new UuidColumn('id', { ui: { hidden: true } }),
13
- created: new DateTimeColumn('created', {
13
+ created: new DateTimeColumn('created', {
14
14
  defaultValue: async () => moment(),
15
15
  }),
16
- updated: new DateTimeColumn('updated', {
17
- defaultValue: async () => moment(),
16
+ updated: new DateTimeColumn('updated', {
17
+ defaultValue: async () => moment(),
18
18
  updateValue: async () => moment(),
19
19
  }),
20
- }
20
+ };
21
21
 
22
22
  /**
23
23
  * Wrapper function to add default Record columns to your table's columns (should always use).
24
- *
24
+ *
25
25
  * Note: using this requires an explicit dependency on moment@2.29.4 in your package (since transient dependencies are brittle by typescript's standards)
26
- *
26
+ *
27
27
  * @param columns your columns
28
28
  * @returns recordColumns & your columns
29
29
  */
30
- export function withRecordColumns<T extends Record>(columns: Columns<Omit<T, keyof Record>>): Columns<Record> & Columns<Omit<T, keyof Record>> {
30
+ export function withRecordColumns<T extends Record>(
31
+ columns: Columns<Omit<T, keyof Record>>
32
+ ): Columns<Record> & Columns<Omit<T, keyof Record>> {
31
33
  return Object.assign(Object.assign({}, recordColumns), columns);
32
34
  }
33
35
 
34
- export type SerializedRecord = { [columnName: string]: any }
36
+ export type SerializedRecord = { [columnName: string]: any };
35
37
 
36
38
  export class RecordSerializer<T extends Record> {
37
39
  private table: Table<T>;
38
-
39
- constructor(table: Table<T>) {
40
+
41
+ constructor(table: Table<T>) {
40
42
  this.table = table;
41
43
  }
42
44
 
43
45
  async serialize(record: any): Promise<SerializedRecord> {
44
46
  const serialized: any = {};
45
47
  const fieldSerializer = new FieldSerializer(this.table);
46
- for (let fieldPropertyName in record) {
47
- if (typeof record[fieldPropertyName] === 'function')
48
+ for (const fieldPropertyName in record) {
49
+ if (typeof record[fieldPropertyName] === 'function') {
48
50
  continue;
49
-
51
+ }
52
+
50
53
  const fieldValue = await record[fieldPropertyName];
51
54
  const { columnName, serializedFieldValue } = await fieldSerializer.serialize(fieldPropertyName, fieldValue);
52
55
  serialized[columnName] = serializedFieldValue;
@@ -57,7 +60,7 @@ export class RecordSerializer<T extends Record> {
57
60
  async deserialize(serializedRecord: SerializedRecord): Promise<T> {
58
61
  const deserialized: any = {};
59
62
  const fieldSerializer = new FieldSerializer(this.table);
60
- for (let columnName in serializedRecord) {
63
+ for (const columnName in serializedRecord) {
61
64
  const serializedFieldValue = serializedRecord[columnName];
62
65
  const { fieldPropertyName, fieldValue } = await fieldSerializer.deserialize(columnName, serializedFieldValue);
63
66
  deserialized[fieldPropertyName] = fieldValue;
@@ -67,29 +70,31 @@ export class RecordSerializer<T extends Record> {
67
70
  }
68
71
 
69
72
  export class FieldSerializer<T extends Record> {
70
- constructor(
71
- private table: Table<T>
72
- ){}
73
+ constructor(private table: Table<T>) {}
73
74
 
74
75
  async serialize(fieldPropertyName: string, fieldValue: any) {
75
- const columns: {[prop: string]: Column<any, any>} = this.table.columns;
76
+ const columns: { [prop: string]: Column<any, any> } = this.table.columns;
76
77
  const column = columns[fieldPropertyName];
77
- if (!column)
78
- throw new Error(`[FieldSerializer.serialize] (${this.table.name}) no column matches fieldPropertyName: ${fieldPropertyName}`);
78
+ if (!column) {
79
+ throw new Error(
80
+ `[FieldSerializer.serialize] (${this.table.name}) no column matches fieldPropertyName: ${fieldPropertyName}`
81
+ );
82
+ }
79
83
 
80
84
  let serializedFieldValue = fieldValue;
81
- if (column.serialize)
85
+ if (column.serialize) {
82
86
  serializedFieldValue = await column.serialize(fieldValue);
87
+ }
83
88
 
84
89
  return { columnName: column.name, serializedFieldValue };
85
90
  }
86
91
 
87
92
  async deserialize(columnName: string, serializedFieldValue: any) {
88
- const columns: {[prop: string]: Column<any, any>} = this.table.columns;
93
+ const columns: { [prop: string]: Column<any, any> } = this.table.columns;
89
94
  let fieldPropertyName = columnName;
90
95
  let column = columns[columnName]; // the scenario that the column name is the same as the property name
91
96
  if (!column) {
92
- for (let columnPropertyName in columns) {
97
+ for (const columnPropertyName in columns) {
93
98
  const checkColumn = (this.table.columns as any)[columnPropertyName];
94
99
  if (checkColumn && columnName == checkColumn.name) {
95
100
  fieldPropertyName = columnPropertyName;
@@ -104,10 +109,11 @@ export class FieldSerializer<T extends Record> {
104
109
  return { fieldPropertyName, fieldValue: undefined };
105
110
  }
106
111
 
107
- let fieldValue = serializedFieldValue
108
- if (column.deserialize)
112
+ let fieldValue = serializedFieldValue;
113
+ if (column.deserialize) {
109
114
  fieldValue = await column.deserialize(serializedFieldValue);
115
+ }
110
116
 
111
117
  return { fieldPropertyName, fieldValue };
112
118
  }
113
- }
119
+ }
@@ -6,31 +6,29 @@ export class StatementConfigFactory {
6
6
  private dbName: string;
7
7
  private getTable: (tableName: string) => Table<any>;
8
8
 
9
- constructor(
10
- dbName: string,
11
- getTable?: (tableName: string) => Table<any>
12
- ) {
9
+ constructor(dbName: string, getTable?: (tableName: string) => Table<any>) {
13
10
  this.dbName = dbName;
14
11
  this.getTable = getTable ? getTable : tableByName;
15
12
  }
16
13
 
17
14
  getStatementConfig(config: DbDriverStatementConfig): StatementConfig {
18
15
  return {
19
- dbName: config.prefixTablesWithDb ? this.dbName : undefined,
20
- resolveFieldName: this.getResolveFieldName(),
21
- useParams: config.useParams,
22
- useNamedParams: config.useNamedParams
16
+ dbName: config.prefixTablesWithDb ? this.dbName : undefined,
17
+ resolveFieldName: this.getResolveFieldName(),
18
+ useParams: config.useParams,
19
+ useNamedParams: config.useNamedParams,
23
20
  };
24
21
  }
25
22
 
26
23
  private getResolveFieldName() {
27
- return (tableName: string, propertyName: string): string => {
28
- const table = this.getTable(tableName);
29
- const column = table.columns[propertyName];
30
- if (!column)
31
- throw new Error(`(${table.name}) Column does not exist for property: ${propertyName}`);
32
-
33
- return column.name;
24
+ return (tableName: string, propertyName: string): string => {
25
+ const table = this.getTable(tableName);
26
+ const column = table.columns[propertyName];
27
+ if (!column) {
28
+ throw new Error(`(${table.name}) Column does not exist for property: ${propertyName}`);
34
29
  }
30
+
31
+ return column.name;
32
+ };
35
33
  }
36
- }
34
+ }