@hedhog/pagination 0.0.16 → 0.0.18
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/README.md +4 -4
- package/dist/databases/abstract.database.d.ts +61 -0
- package/dist/databases/abstract.database.d.ts.map +1 -0
- package/dist/databases/abstract.database.js +643 -0
- package/dist/databases/abstract.database.js.map +1 -0
- package/dist/databases/database.d.ts +5 -0
- package/dist/databases/database.d.ts.map +1 -0
- package/dist/databases/database.factory.d.ts +7 -0
- package/dist/databases/database.factory.d.ts.map +1 -0
- package/dist/databases/database.factory.js +44 -0
- package/dist/databases/database.factory.js.map +1 -0
- package/dist/databases/database.js +9 -0
- package/dist/databases/database.js.map +1 -0
- package/dist/databases/index.d.ts +4 -0
- package/dist/databases/index.d.ts.map +1 -0
- package/dist/databases/index.js +20 -0
- package/dist/databases/index.js.map +1 -0
- package/dist/databases/mysql.database.d.ts +10 -0
- package/dist/databases/mysql.database.d.ts.map +1 -0
- package/dist/databases/mysql.database.js +17 -0
- package/dist/databases/mysql.database.js.map +1 -0
- package/dist/databases/postgres.database.d.ts +10 -0
- package/dist/databases/postgres.database.d.ts.map +1 -0
- package/dist/databases/postgres.database.js +17 -0
- package/dist/databases/postgres.database.js.map +1 -0
- package/dist/pagination.service.d.ts +16 -2
- package/dist/pagination.service.d.ts.map +1 -1
- package/dist/pagination.service.js +131 -5
- package/dist/pagination.service.js.map +1 -1
- package/dist/types/pagination.types.d.ts +1 -0
- package/dist/types/pagination.types.d.ts.map +1 -1
- package/dist/types/query-option.d.ts +5 -0
- package/dist/types/query-option.d.ts.map +1 -0
- package/dist/types/query-option.js +3 -0
- package/dist/types/query-option.js.map +1 -0
- package/dist/types/relation-n2n-result.d.ts +7 -0
- package/dist/types/relation-n2n-result.d.ts.map +1 -0
- package/dist/types/relation-n2n-result.js +3 -0
- package/dist/types/relation-n2n-result.js.map +1 -0
- package/dist/types/transaction-queries.d.ts +7 -0
- package/dist/types/transaction-queries.d.ts.map +1 -0
- package/dist/types/transaction-queries.js +3 -0
- package/dist/types/transaction-queries.js.map +1 -0
- package/package.json +6 -2
- package/src/databases/abstract.database.ts +833 -0
- package/src/databases/database.factory.ts +26 -0
- package/src/databases/database.ts +4 -0
- package/src/databases/index.ts +3 -0
- package/src/databases/mysql.database.ts +14 -0
- package/src/databases/postgres.database.ts +14 -0
- package/src/pagination.service.ts +216 -12
- package/src/types/pagination.types.ts +1 -0
- package/src/types/query-option.ts +4 -0
- package/src/types/relation-n2n-result.ts +6 -0
- package/src/types/transaction-queries.ts +7 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
import * as chalk from 'chalk';
|
2
|
+
import { Database } from './database';
|
3
|
+
import { PostgresDatabase } from './postgres.database';
|
4
|
+
import { MySQLDatabase } from './mysql.database';
|
5
|
+
|
6
|
+
export class DatabaseFactory {
|
7
|
+
public static create(
|
8
|
+
type: Database,
|
9
|
+
host: string,
|
10
|
+
user: string,
|
11
|
+
password: string,
|
12
|
+
database: string,
|
13
|
+
port: number,
|
14
|
+
) {
|
15
|
+
switch (type) {
|
16
|
+
case Database.POSTGRES:
|
17
|
+
return new PostgresDatabase(host, user, password, database, port);
|
18
|
+
|
19
|
+
case Database.MYSQL:
|
20
|
+
return new MySQLDatabase(host, user, password, database, port);
|
21
|
+
|
22
|
+
default:
|
23
|
+
console.info(chalk.yellow(`[WARN] Unsupported Database: ${type}`));
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { AbstractDatabase } from './abstract.database';
|
2
|
+
import { Database } from './database';
|
3
|
+
|
4
|
+
export class MySQLDatabase extends AbstractDatabase {
|
5
|
+
constructor(
|
6
|
+
protected host: string,
|
7
|
+
protected user: string,
|
8
|
+
protected password: string,
|
9
|
+
protected database: string,
|
10
|
+
protected port: number,
|
11
|
+
) {
|
12
|
+
super(Database.MYSQL, host, user, password, database, port);
|
13
|
+
}
|
14
|
+
}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { AbstractDatabase } from './abstract.database';
|
2
|
+
import { Database } from './database';
|
3
|
+
|
4
|
+
export class PostgresDatabase extends AbstractDatabase {
|
5
|
+
constructor(
|
6
|
+
protected host: string,
|
7
|
+
protected user: string,
|
8
|
+
protected password: string,
|
9
|
+
protected database: string,
|
10
|
+
protected port: number,
|
11
|
+
) {
|
12
|
+
super(Database.POSTGRES, host, user, password, database, port);
|
13
|
+
}
|
14
|
+
}
|
@@ -1,27 +1,27 @@
|
|
1
|
-
import { itemTranslations } from '@hedhog/
|
1
|
+
import { itemTranslations } from '@hedhog/core';
|
2
2
|
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
|
3
3
|
import {
|
4
4
|
DEFAULT_PAGE,
|
5
5
|
DEFAULT_PAGE_SIZE,
|
6
6
|
} from './constants/pagination.constants';
|
7
|
+
import { AbstractDatabase, Database, DatabaseFactory } from './databases';
|
7
8
|
import { PageOrderDirection } from './enums/patination.enums';
|
8
9
|
import type {
|
9
10
|
BaseModel,
|
10
11
|
FindManyArgs,
|
11
|
-
PaginatedResult,
|
12
12
|
PaginationParams,
|
13
13
|
} from './types/pagination.types';
|
14
14
|
|
15
15
|
@Injectable()
|
16
16
|
export class PaginationService {
|
17
17
|
private readonly logger = new Logger(PaginationService.name);
|
18
|
-
|
18
|
+
private db: any = null;
|
19
19
|
async paginate<T, M extends BaseModel>(
|
20
20
|
model: M,
|
21
21
|
paginationParams: PaginationParams,
|
22
22
|
customQuery?: FindManyArgs<M>,
|
23
23
|
translationKey?: string,
|
24
|
-
)
|
24
|
+
) /*: Promise<PaginatedResult<T>>*/ {
|
25
25
|
try {
|
26
26
|
if (!model) {
|
27
27
|
throw new BadRequestException('Model is required');
|
@@ -49,16 +49,25 @@ export class PaginationService {
|
|
49
49
|
|
50
50
|
if (sortField) {
|
51
51
|
const invalid = this.isInvalidField(sortField, model);
|
52
|
+
let localeInvalid = false;
|
52
53
|
if (invalid) {
|
53
|
-
this.
|
54
|
-
throw new BadRequestException(
|
55
|
-
`Invalid field: ${sortField}. Valid columns are: ${this.extractFieldNames(
|
56
|
-
model,
|
57
|
-
).join(', ')}`,
|
58
|
-
);
|
59
|
-
}
|
54
|
+
localeInvalid = this.isInvalidLocaleField(sortField, model);
|
60
55
|
|
61
|
-
|
56
|
+
if (localeInvalid) {
|
57
|
+
this.logger.error(`Invalid field: ${sortField}`);
|
58
|
+
throw new BadRequestException(
|
59
|
+
`Invalid field: ${sortField}. Valid columns are: ${this.extractFieldNames(
|
60
|
+
model,
|
61
|
+
).join(', ')}`,
|
62
|
+
);
|
63
|
+
} else {
|
64
|
+
sortOrderCondition = {
|
65
|
+
[`${model.name}_locale`]: { [sortField]: sortOrder },
|
66
|
+
};
|
67
|
+
}
|
68
|
+
} else {
|
69
|
+
sortOrderCondition = { [sortField]: sortOrder };
|
70
|
+
}
|
62
71
|
}
|
63
72
|
|
64
73
|
if (search) {
|
@@ -117,6 +126,7 @@ export class PaginationService {
|
|
117
126
|
let [total, data] = await Promise.all([
|
118
127
|
model.count({ where: customQuery?.where || {} }),
|
119
128
|
model.findMany(query),
|
129
|
+
//this.query(model, query),
|
120
130
|
]);
|
121
131
|
|
122
132
|
const lastPage = Math.ceil(total / pageSize);
|
@@ -163,9 +173,203 @@ export class PaginationService {
|
|
163
173
|
return model && model.fields ? !model.fields[sortField] : true;
|
164
174
|
}
|
165
175
|
|
176
|
+
isInvalidLocaleField(sortField: string, model: BaseModel): boolean {
|
177
|
+
const fields = model['$parent'][`${model.name}_locale`].fields;
|
178
|
+
|
179
|
+
return model && fields ? !fields[sortField] : true;
|
180
|
+
}
|
181
|
+
|
166
182
|
isInvalidFields(fields: string[], model: BaseModel): boolean {
|
167
183
|
return !fields.every((field) =>
|
168
184
|
model.fields ? model && model.fields[field] : false,
|
169
185
|
);
|
170
186
|
}
|
187
|
+
|
188
|
+
isInvalidLocaleFields(fields: string[], model: BaseModel): boolean {
|
189
|
+
const localeFields = model['$parent'][`${model.name}_locale`].fields;
|
190
|
+
|
191
|
+
return !fields.every((field) =>
|
192
|
+
localeFields ? !localeFields[field] : false,
|
193
|
+
);
|
194
|
+
}
|
195
|
+
|
196
|
+
async getDb(model: any): Promise<any> {
|
197
|
+
const {
|
198
|
+
DATABASE_URL,
|
199
|
+
DB_HOST,
|
200
|
+
DB_PORT,
|
201
|
+
DB_USERNAME,
|
202
|
+
DB_PASSWORD,
|
203
|
+
DB_DATABASE,
|
204
|
+
} = model['$parent']._engine.config.env;
|
205
|
+
|
206
|
+
const type = DATABASE_URL.split(':')[0];
|
207
|
+
|
208
|
+
this.db = DatabaseFactory.create(
|
209
|
+
type === 'mysql' ? Database.MYSQL : Database.POSTGRES,
|
210
|
+
DB_HOST,
|
211
|
+
DB_USERNAME,
|
212
|
+
DB_PASSWORD,
|
213
|
+
DB_DATABASE,
|
214
|
+
Number(DB_PORT),
|
215
|
+
);
|
216
|
+
|
217
|
+
return this.db;
|
218
|
+
}
|
219
|
+
|
220
|
+
async getBuilder(
|
221
|
+
model: any,
|
222
|
+
tableName: string,
|
223
|
+
query: any,
|
224
|
+
builder: any,
|
225
|
+
): Promise<any> {
|
226
|
+
const db = await this.getDb(model);
|
227
|
+
|
228
|
+
if (!builder) {
|
229
|
+
builder = {
|
230
|
+
joinTables: [],
|
231
|
+
select: [],
|
232
|
+
where: [],
|
233
|
+
order: [],
|
234
|
+
join: [],
|
235
|
+
from: db.getColumnNameWithScaping(tableName),
|
236
|
+
};
|
237
|
+
}
|
238
|
+
|
239
|
+
if (query.orderBy) {
|
240
|
+
for (const key in query.orderBy) {
|
241
|
+
if (typeof query.orderBy[key] === 'object') {
|
242
|
+
if (!builder.joinTables.includes(key)) {
|
243
|
+
builder.joinTables.push(key);
|
244
|
+
const foreignKey = await db.getColumnNameFromRelation(
|
245
|
+
tableName,
|
246
|
+
`${tableName}_locale`,
|
247
|
+
);
|
248
|
+
|
249
|
+
const primaryKeys = await db.getPrimaryKeys(tableName);
|
250
|
+
|
251
|
+
if (primaryKeys.length !== 1) {
|
252
|
+
throw new Error('Only single primary key is supported');
|
253
|
+
}
|
254
|
+
|
255
|
+
const primaryKey = primaryKeys[0];
|
256
|
+
|
257
|
+
builder.join.push(
|
258
|
+
`LEFT JOIN ${db.getColumnNameWithScaping(key)} ON ${db.getColumnNameWithScaping(key)}.${db.getColumnNameWithScaping(foreignKey)} = ${db.getColumnNameWithScaping(tableName)}.${db.getColumnNameWithScaping(primaryKey)}`,
|
259
|
+
);
|
260
|
+
|
261
|
+
for (const k in query.orderBy[key]) {
|
262
|
+
builder.order.push(
|
263
|
+
`${db.getColumnNameWithScaping(key)}.${db.getColumnNameWithScaping(k)} ${query.orderBy[key][k]}`,
|
264
|
+
);
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
if (query.select) {
|
272
|
+
builder.select = [
|
273
|
+
...builder.select,
|
274
|
+
...Object.keys(query.select).map(
|
275
|
+
(key) =>
|
276
|
+
`${db.getColumnNameWithScaping(tableName)}.${db.getColumnNameWithScaping(key)}`,
|
277
|
+
),
|
278
|
+
];
|
279
|
+
for (const key in query.select) {
|
280
|
+
if (typeof query.select[key] === 'object') {
|
281
|
+
builder = await this.getBuilder(
|
282
|
+
model,
|
283
|
+
key,
|
284
|
+
query.select[key],
|
285
|
+
builder,
|
286
|
+
);
|
287
|
+
}
|
288
|
+
}
|
289
|
+
} else if (query.include) {
|
290
|
+
builder.select = [
|
291
|
+
...builder.select,
|
292
|
+
`${db.getColumnNameWithScaping(tableName)}.*`,
|
293
|
+
];
|
294
|
+
for (const key in query.include) {
|
295
|
+
if (typeof query.include[key] === 'object') {
|
296
|
+
if (!builder.joinTables.includes(key)) {
|
297
|
+
builder.joinTables.push(key);
|
298
|
+
|
299
|
+
const foreignKey = await db.getColumnNameFromRelation(
|
300
|
+
tableName,
|
301
|
+
key,
|
302
|
+
);
|
303
|
+
|
304
|
+
const primaryKeys = await db.getPrimaryKeys(tableName);
|
305
|
+
|
306
|
+
const primaryKey = primaryKeys[0];
|
307
|
+
|
308
|
+
builder.join.push(
|
309
|
+
`LEFT JOIN ${db.getColumnNameWithScaping(key)} ON ${db.getColumnNameWithScaping(key)}.${db.getColumnNameWithScaping(foreignKey)} = ${db.getColumnNameWithScaping(tableName)}.${db.getColumnNameWithScaping(primaryKey)}`,
|
310
|
+
);
|
311
|
+
|
312
|
+
builder = await this.getBuilder(
|
313
|
+
model,
|
314
|
+
key,
|
315
|
+
query.include[key],
|
316
|
+
builder,
|
317
|
+
);
|
318
|
+
}
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
if (query.where) {
|
324
|
+
for (const key in query.where) {
|
325
|
+
if (typeof query.where[key] === 'object') {
|
326
|
+
if (!builder.joinTables.includes(key)) {
|
327
|
+
builder.joinTables.push(key);
|
328
|
+
const foreignKey = await db.getColumnNameFromRelation(
|
329
|
+
key,
|
330
|
+
tableName,
|
331
|
+
);
|
332
|
+
const primaryKeys = await db.getPrimaryKeys(key);
|
333
|
+
|
334
|
+
builder.join.push(
|
335
|
+
`LEFT JOIN ${db.getColumnNameWithScaping(key)} ON ${db.getColumnNameWithScaping(key)}.${db.getColumnNameWithScaping(primaryKeys[0])} = ${db.getColumnNameWithScaping(tableName)}.${db.getColumnNameWithScaping(foreignKey)}`,
|
336
|
+
);
|
337
|
+
|
338
|
+
for (const k in query.where[key]) {
|
339
|
+
builder.where.push(
|
340
|
+
`${db.getColumnNameWithScaping(key)}.${db.getColumnNameWithScaping(k)} = ${AbstractDatabase.addSimpleQuotes(query.where[key][k])}`,
|
341
|
+
);
|
342
|
+
}
|
343
|
+
}
|
344
|
+
} else {
|
345
|
+
builder.where.push(
|
346
|
+
`${db.getColumnNameWithScaping(tableName)}.${db.getColumnNameWithScaping(key)} = ${AbstractDatabase.addSimpleQuotes(query.where[key])}`,
|
347
|
+
);
|
348
|
+
}
|
349
|
+
}
|
350
|
+
}
|
351
|
+
|
352
|
+
return builder;
|
353
|
+
}
|
354
|
+
|
355
|
+
async query(model: any, query: any): Promise<any[]> {
|
356
|
+
const db = await this.getDb(model);
|
357
|
+
const builder = await this.getBuilder(model, model.name, query, null);
|
358
|
+
|
359
|
+
const sql = [];
|
360
|
+
|
361
|
+
sql.push(`SELECT ${builder.select.join(', ')}`);
|
362
|
+
sql.push(`FROM ${builder.from}`);
|
363
|
+
if (builder.join.length) sql.push(builder.join.join(' '));
|
364
|
+
if (builder.where.length) sql.push(`WHERE ${builder.where.join(' AND ')}`);
|
365
|
+
if (builder.order.length) sql.push(`ORDER BY ${builder.order.join(', ')}`);
|
366
|
+
if (query.take >= 0 && query.skip >= 0)
|
367
|
+
sql.push(db.getLimit(query.take, query.skip));
|
368
|
+
|
369
|
+
console.log('sql', sql.join(' '));
|
370
|
+
|
371
|
+
const result = await db.query(sql.join(' '));
|
372
|
+
|
373
|
+
return result;
|
374
|
+
}
|
171
375
|
}
|