@vritti/api-sdk 0.0.9 → 0.1.0
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/dist/drizzle-orm.cjs +25 -0
- package/dist/drizzle-orm.cjs.map +1 -0
- package/dist/drizzle-orm.d.cts +2 -0
- package/dist/drizzle-orm.d.ts +2 -0
- package/dist/drizzle-orm.js +3 -0
- package/dist/drizzle-orm.js.map +1 -0
- package/dist/drizzle-pg-core.cjs +25 -0
- package/dist/drizzle-pg-core.cjs.map +1 -0
- package/dist/drizzle-pg-core.d.cts +1 -0
- package/dist/drizzle-pg-core.d.ts +1 -0
- package/dist/drizzle-pg-core.js +3 -0
- package/dist/drizzle-pg-core.js.map +1 -0
- package/dist/index.cjs +317 -279
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +318 -215
- package/dist/index.d.ts +318 -215
- package/dist/index.js +317 -279
- package/dist/index.js.map +1 -1
- package/package.json +33 -10
package/dist/index.cjs
CHANGED
|
@@ -222,6 +222,9 @@ var jwt = __toESM(require("jsonwebtoken"), 1);
|
|
|
222
222
|
|
|
223
223
|
// src/database/services/primary-database.service.ts
|
|
224
224
|
var import_common3 = require("@nestjs/common");
|
|
225
|
+
var import_pg = require("pg");
|
|
226
|
+
var import_node_postgres = require("drizzle-orm/node-postgres");
|
|
227
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
225
228
|
|
|
226
229
|
// src/database/constants.ts
|
|
227
230
|
var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
@@ -250,8 +253,10 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
250
253
|
}
|
|
251
254
|
options;
|
|
252
255
|
logger = new import_common3.Logger(_PrimaryDatabaseService.name);
|
|
253
|
-
/**
|
|
254
|
-
|
|
256
|
+
/** PostgreSQL connection pool */
|
|
257
|
+
pool = null;
|
|
258
|
+
/** Drizzle database instance */
|
|
259
|
+
db = null;
|
|
255
260
|
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
256
261
|
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
257
262
|
/** Cache TTL in milliseconds */
|
|
@@ -262,28 +267,24 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
262
267
|
}
|
|
263
268
|
async onModuleInit() {
|
|
264
269
|
if (this.options.primaryDb) {
|
|
265
|
-
await this.
|
|
270
|
+
await this.initializeDrizzleClient();
|
|
266
271
|
}
|
|
267
272
|
}
|
|
268
273
|
/**
|
|
269
|
-
* Initialize connection to primary database
|
|
274
|
+
* Initialize connection to primary database using Drizzle
|
|
270
275
|
*/
|
|
271
|
-
async
|
|
276
|
+
async initializeDrizzleClient() {
|
|
272
277
|
try {
|
|
273
|
-
const PrimaryDbClient = this.options.prismaClientConstructor;
|
|
274
278
|
const databaseUrl = this.buildPrimaryDbUrl();
|
|
275
|
-
this.
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
"error",
|
|
283
|
-
"warn"
|
|
284
|
-
]
|
|
279
|
+
this.pool = new import_pg.Pool({
|
|
280
|
+
connectionString: databaseUrl,
|
|
281
|
+
max: this.options.maxConnections || 10
|
|
282
|
+
});
|
|
283
|
+
this.db = (0, import_node_postgres.drizzle)({
|
|
284
|
+
client: this.pool,
|
|
285
|
+
schema: this.options.drizzleSchema
|
|
285
286
|
});
|
|
286
|
-
await this.
|
|
287
|
+
await this.pool.query("SELECT 1");
|
|
287
288
|
this.logger.log("Connected to primary database (tenant registry)");
|
|
288
289
|
} catch (error) {
|
|
289
290
|
this.logger.error("Failed to connect to primary database", error);
|
|
@@ -298,7 +299,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
298
299
|
throw new Error("Primary database configuration not provided");
|
|
299
300
|
}
|
|
300
301
|
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
301
|
-
let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
|
|
302
|
+
let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
302
303
|
const params = new URLSearchParams();
|
|
303
304
|
if (schema) {
|
|
304
305
|
params.set("schema", schema);
|
|
@@ -318,9 +319,9 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
318
319
|
return url.replace(/:([^@]+)@/, ":****@");
|
|
319
320
|
}
|
|
320
321
|
/**
|
|
321
|
-
* Get tenant configuration by identifier (ID or
|
|
322
|
+
* Get tenant configuration by identifier (ID or subdomain)
|
|
322
323
|
*
|
|
323
|
-
* @param tenantIdentifier Tenant ID or
|
|
324
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
324
325
|
* @returns Tenant configuration or null if not found
|
|
325
326
|
*/
|
|
326
327
|
async getTenantInfo(tenantIdentifier) {
|
|
@@ -330,45 +331,39 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
330
331
|
return cached;
|
|
331
332
|
}
|
|
332
333
|
try {
|
|
333
|
-
if (!this.
|
|
334
|
+
if (!this.db) {
|
|
334
335
|
throw new Error("Primary database client not initialized");
|
|
335
336
|
}
|
|
336
337
|
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
id: tenantIdentifier
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
subdomain: tenantIdentifier
|
|
345
|
-
}
|
|
346
|
-
],
|
|
347
|
-
status: "ACTIVE"
|
|
348
|
-
},
|
|
349
|
-
include: {
|
|
350
|
-
databaseConfig: true
|
|
351
|
-
}
|
|
352
|
-
});
|
|
353
|
-
if (!tenant) {
|
|
338
|
+
const schema = this.options.drizzleSchema;
|
|
339
|
+
const { tenants, tenantDatabaseConfigs } = schema;
|
|
340
|
+
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, (0, import_drizzle_orm.eq)(tenants.id, tenantDatabaseConfigs.tenantId)).where((0, import_drizzle_orm.or)((0, import_drizzle_orm.eq)(tenants.id, tenantIdentifier), (0, import_drizzle_orm.eq)(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
341
|
+
if (!result.length) {
|
|
354
342
|
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
355
343
|
return null;
|
|
356
344
|
}
|
|
345
|
+
const row = result[0];
|
|
346
|
+
const tenant = row.tenants;
|
|
347
|
+
const config = row.tenant_database_configs;
|
|
348
|
+
if (tenant.status !== "ACTIVE") {
|
|
349
|
+
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
357
352
|
const info = {
|
|
358
353
|
id: tenant.id,
|
|
359
354
|
subdomain: tenant.subdomain,
|
|
360
355
|
type: tenant.dbType,
|
|
361
356
|
status: tenant.status,
|
|
362
357
|
// For SHARED tenants: schema name
|
|
363
|
-
schemaName:
|
|
358
|
+
schemaName: config?.dbSchema || void 0,
|
|
364
359
|
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
365
|
-
databaseName:
|
|
366
|
-
databaseHost:
|
|
367
|
-
databasePort:
|
|
368
|
-
databaseUsername:
|
|
369
|
-
databasePassword:
|
|
370
|
-
databaseSslMode:
|
|
371
|
-
connectionPoolSize:
|
|
360
|
+
databaseName: config?.dbName || void 0,
|
|
361
|
+
databaseHost: config?.dbHost || void 0,
|
|
362
|
+
databasePort: config?.dbPort || void 0,
|
|
363
|
+
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
364
|
+
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
365
|
+
databaseSslMode: config?.dbSslMode || void 0,
|
|
366
|
+
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
372
367
|
};
|
|
373
368
|
this.cacheInfo(info);
|
|
374
369
|
return info;
|
|
@@ -394,7 +389,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
394
389
|
*
|
|
395
390
|
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
396
391
|
*
|
|
397
|
-
* @param tenantIdentifier Tenant ID or
|
|
392
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
398
393
|
*/
|
|
399
394
|
clearTenantCache(tenantIdentifier) {
|
|
400
395
|
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
@@ -413,17 +408,23 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
413
408
|
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
414
409
|
}
|
|
415
410
|
/**
|
|
416
|
-
* Get the
|
|
417
|
-
* This is a synchronous property that returns the initialized
|
|
411
|
+
* Get the Drizzle database instance for the primary database.
|
|
412
|
+
* This is a synchronous property that returns the initialized Drizzle client.
|
|
418
413
|
*
|
|
419
|
-
* @returns Primary database
|
|
414
|
+
* @returns Primary database Drizzle instance
|
|
420
415
|
* @throws Error if primary database client is not initialized
|
|
421
416
|
*/
|
|
422
|
-
get
|
|
423
|
-
if (!this.
|
|
417
|
+
get drizzleClient() {
|
|
418
|
+
if (!this.db) {
|
|
424
419
|
throw new Error("Primary database client not initialized");
|
|
425
420
|
}
|
|
426
|
-
return this.
|
|
421
|
+
return this.db;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get the Drizzle schema
|
|
425
|
+
*/
|
|
426
|
+
get schema() {
|
|
427
|
+
return this.options.drizzleSchema;
|
|
427
428
|
}
|
|
428
429
|
/**
|
|
429
430
|
* Decrypt database credentials
|
|
@@ -437,8 +438,8 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
437
438
|
return encrypted;
|
|
438
439
|
}
|
|
439
440
|
async onModuleDestroy() {
|
|
440
|
-
if (this.
|
|
441
|
-
await this.
|
|
441
|
+
if (this.pool) {
|
|
442
|
+
await this.pool.end();
|
|
442
443
|
this.logger.log("Disconnected from primary database");
|
|
443
444
|
}
|
|
444
445
|
}
|
|
@@ -983,6 +984,8 @@ TenantContextInterceptor = _ts_decorate8([
|
|
|
983
984
|
|
|
984
985
|
// src/database/services/tenant-database.service.ts
|
|
985
986
|
var import_common9 = require("@nestjs/common");
|
|
987
|
+
var import_pg2 = require("pg");
|
|
988
|
+
var import_node_postgres2 = require("drizzle-orm/node-postgres");
|
|
986
989
|
function _ts_decorate9(decorators, target, key, desc) {
|
|
987
990
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
988
991
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -1007,7 +1010,7 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1007
1010
|
options;
|
|
1008
1011
|
tenantContext;
|
|
1009
1012
|
logger = new import_common9.Logger(_TenantDatabaseService.name);
|
|
1010
|
-
/** Connection pool: Map<cacheKey,
|
|
1013
|
+
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
1011
1014
|
clients = /* @__PURE__ */ new Map();
|
|
1012
1015
|
/** Track last usage time for idle connection cleanup */
|
|
1013
1016
|
clientLastUsed = /* @__PURE__ */ new Map();
|
|
@@ -1019,17 +1022,23 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1019
1022
|
this.startConnectionCleaner();
|
|
1020
1023
|
}
|
|
1021
1024
|
/**
|
|
1022
|
-
* Get the
|
|
1025
|
+
* Get the Drizzle client for the current tenant's database.
|
|
1023
1026
|
* This returns the tenant-scoped database client.
|
|
1024
1027
|
*
|
|
1025
|
-
* @returns Tenant-scoped database
|
|
1028
|
+
* @returns Tenant-scoped Drizzle database instance
|
|
1026
1029
|
* @throws UnauthorizedException if tenant context not set
|
|
1027
1030
|
* @throws InternalServerErrorException if connection fails
|
|
1028
1031
|
*/
|
|
1029
|
-
get
|
|
1032
|
+
get drizzleClient() {
|
|
1030
1033
|
return this.getDbClient();
|
|
1031
1034
|
}
|
|
1032
1035
|
/**
|
|
1036
|
+
* Get the Drizzle schema
|
|
1037
|
+
*/
|
|
1038
|
+
get schema() {
|
|
1039
|
+
return this.options.drizzleSchema;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1033
1042
|
* Get tenant-scoped database client for the current request/message
|
|
1034
1043
|
*
|
|
1035
1044
|
* This method:
|
|
@@ -1037,66 +1046,61 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1037
1046
|
* 2. Builds a connection URL based on tenant type
|
|
1038
1047
|
* 3. Returns cached client if exists, otherwise creates new one
|
|
1039
1048
|
*
|
|
1040
|
-
* @returns
|
|
1049
|
+
* @returns Drizzle database instance
|
|
1041
1050
|
* @throws UnauthorizedException if tenant context not set
|
|
1042
1051
|
* @throws InternalServerErrorException if connection fails
|
|
1043
|
-
*
|
|
1044
|
-
* @example
|
|
1045
|
-
* const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
|
|
1046
|
-
* const users = await dbClient.user.findMany();
|
|
1047
1052
|
*/
|
|
1048
|
-
|
|
1053
|
+
getDbClient() {
|
|
1049
1054
|
const tenant = this.tenantContext.getTenant();
|
|
1050
1055
|
const cacheKey = this.buildCacheKey(tenant);
|
|
1051
|
-
|
|
1056
|
+
const existing = this.clients.get(cacheKey);
|
|
1057
|
+
if (existing) {
|
|
1052
1058
|
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1053
1059
|
this.logger.debug(`Reusing cached connection: ${cacheKey}`);
|
|
1054
|
-
return
|
|
1060
|
+
return existing.db;
|
|
1055
1061
|
}
|
|
1056
1062
|
this.logger.log(`Creating new database connection: ${cacheKey}`);
|
|
1057
|
-
const
|
|
1058
|
-
this.clients.set(cacheKey,
|
|
1063
|
+
const connection = this.createDbClientSync(tenant);
|
|
1064
|
+
this.clients.set(cacheKey, connection);
|
|
1059
1065
|
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1060
|
-
return
|
|
1066
|
+
return connection.db;
|
|
1061
1067
|
}
|
|
1062
1068
|
/**
|
|
1063
|
-
* Create a new database client for the given tenant
|
|
1069
|
+
* Create a new database client for the given tenant (synchronous)
|
|
1064
1070
|
*/
|
|
1065
|
-
|
|
1071
|
+
createDbClientSync(tenant) {
|
|
1066
1072
|
try {
|
|
1067
1073
|
const databaseUrl = this.buildTenantDbUrl(tenant);
|
|
1068
|
-
const
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
log: [
|
|
1076
|
-
"error",
|
|
1077
|
-
"warn"
|
|
1078
|
-
]
|
|
1074
|
+
const pool = new import_pg2.Pool({
|
|
1075
|
+
connectionString: databaseUrl,
|
|
1076
|
+
max: tenant.connectionPoolSize || this.options.maxConnections || 10
|
|
1077
|
+
});
|
|
1078
|
+
const db = (0, import_node_postgres2.drizzle)({
|
|
1079
|
+
client: pool,
|
|
1080
|
+
schema: this.options.drizzleSchema
|
|
1079
1081
|
});
|
|
1080
|
-
await client.$connect();
|
|
1081
1082
|
this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
|
|
1082
|
-
return
|
|
1083
|
+
return {
|
|
1084
|
+
pool,
|
|
1085
|
+
db
|
|
1086
|
+
};
|
|
1083
1087
|
} catch (error) {
|
|
1084
1088
|
this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
|
|
1085
1089
|
throw new import_common9.InternalServerErrorException("Failed to connect to tenant database");
|
|
1086
1090
|
}
|
|
1087
1091
|
}
|
|
1088
1092
|
/**
|
|
1089
|
-
* Build connection URL for
|
|
1093
|
+
* Build connection URL for tenant (dedicated database)
|
|
1090
1094
|
*/
|
|
1091
1095
|
buildTenantDbUrl(tenant) {
|
|
1092
1096
|
const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
|
|
1093
1097
|
if (!databaseHost || !databaseName || !databaseUsername) {
|
|
1094
|
-
throw new Error(`
|
|
1098
|
+
throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
|
|
1095
1099
|
}
|
|
1096
1100
|
const port = databasePort || 5432;
|
|
1097
1101
|
const sslMode = databaseSslMode || "require";
|
|
1098
|
-
const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1099
|
-
this.logger.debug(`
|
|
1102
|
+
const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1103
|
+
this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
|
|
1100
1104
|
return connectionUrl;
|
|
1101
1105
|
}
|
|
1102
1106
|
/**
|
|
@@ -1118,19 +1122,20 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1118
1122
|
/**
|
|
1119
1123
|
* Clean up idle connections that haven't been used recently
|
|
1120
1124
|
*/
|
|
1121
|
-
cleanupIdleConnections() {
|
|
1125
|
+
async cleanupIdleConnections() {
|
|
1122
1126
|
const now = Date.now();
|
|
1123
1127
|
const maxIdle = this.options.connectionCacheTTL || 3e5;
|
|
1124
1128
|
let cleaned = 0;
|
|
1125
1129
|
for (const [key, lastUsed] of this.clientLastUsed.entries()) {
|
|
1126
1130
|
if (now - lastUsed > maxIdle) {
|
|
1127
|
-
const
|
|
1128
|
-
if (
|
|
1129
|
-
|
|
1131
|
+
const connection = this.clients.get(key);
|
|
1132
|
+
if (connection) {
|
|
1133
|
+
try {
|
|
1134
|
+
await connection.pool.end();
|
|
1130
1135
|
this.logger.debug(`Cleaned up idle connection: ${key}`);
|
|
1131
|
-
}
|
|
1136
|
+
} catch (error) {
|
|
1132
1137
|
this.logger.error(`Error disconnecting idle client: ${key}`, error);
|
|
1133
|
-
}
|
|
1138
|
+
}
|
|
1134
1139
|
this.clients.delete(key);
|
|
1135
1140
|
this.clientLastUsed.delete(key);
|
|
1136
1141
|
cleaned++;
|
|
@@ -1161,9 +1166,9 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1161
1166
|
clearInterval(this.cleanupInterval);
|
|
1162
1167
|
}
|
|
1163
1168
|
this.logger.log(`Disconnecting ${this.clients.size} database connections`);
|
|
1164
|
-
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key,
|
|
1169
|
+
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
|
|
1165
1170
|
try {
|
|
1166
|
-
await
|
|
1171
|
+
await connection.pool.end();
|
|
1167
1172
|
this.logger.debug(`Disconnected: ${key}`);
|
|
1168
1173
|
} catch (error) {
|
|
1169
1174
|
this.logger.error(`Error disconnecting client: ${key}`, error);
|
|
@@ -1299,56 +1304,64 @@ DatabaseModule = _ts_decorate10([
|
|
|
1299
1304
|
|
|
1300
1305
|
// src/database/repositories/primary-base.repository.ts
|
|
1301
1306
|
var import_common11 = require("@nestjs/common");
|
|
1307
|
+
var import_drizzle_orm2 = require("drizzle-orm");
|
|
1302
1308
|
var PrimaryBaseRepository = class {
|
|
1303
1309
|
static {
|
|
1304
1310
|
__name(this, "PrimaryBaseRepository");
|
|
1305
1311
|
}
|
|
1306
1312
|
database;
|
|
1313
|
+
table;
|
|
1307
1314
|
logger;
|
|
1308
|
-
modelGetter;
|
|
1309
1315
|
/**
|
|
1310
|
-
*
|
|
1316
|
+
* The table name extracted from the Drizzle table at runtime.
|
|
1317
|
+
* Used to access the query API for this repository's table.
|
|
1318
|
+
*/
|
|
1319
|
+
tableName;
|
|
1320
|
+
/**
|
|
1321
|
+
* Lazy getter for Drizzle client.
|
|
1311
1322
|
* Accesses the client from the database service only when needed,
|
|
1312
1323
|
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1313
1324
|
*/
|
|
1314
|
-
get
|
|
1315
|
-
return this.database.
|
|
1325
|
+
get db() {
|
|
1326
|
+
return this.database.drizzleClient;
|
|
1316
1327
|
}
|
|
1317
1328
|
/**
|
|
1318
|
-
*
|
|
1319
|
-
*
|
|
1329
|
+
* Model query API for THIS repository's table (Prisma-like syntax)
|
|
1330
|
+
* Scoped to only the table this repository manages.
|
|
1331
|
+
* Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
|
|
1332
|
+
*
|
|
1333
|
+
* @example
|
|
1334
|
+
* ```typescript
|
|
1335
|
+
* // Use relational queries with type safety
|
|
1336
|
+
* const user = await this.model.findFirst({
|
|
1337
|
+
* where: eq(users.id, id),
|
|
1338
|
+
* with: { posts: true, profile: true }
|
|
1339
|
+
* });
|
|
1340
|
+
* ```
|
|
1320
1341
|
*/
|
|
1321
1342
|
get model() {
|
|
1322
|
-
return this.
|
|
1343
|
+
return this.database.drizzleClient.query[this.tableName];
|
|
1323
1344
|
}
|
|
1324
1345
|
/**
|
|
1325
1346
|
* Create a new repository instance
|
|
1326
1347
|
*
|
|
1327
1348
|
* @param database - The primary database service
|
|
1328
|
-
* @param
|
|
1349
|
+
* @param table - The Drizzle table schema object
|
|
1329
1350
|
*
|
|
1330
1351
|
* @example
|
|
1331
1352
|
* ```typescript
|
|
1332
|
-
*
|
|
1333
|
-
* constructor(database: PrimaryDatabaseService) {
|
|
1334
|
-
* super(database, (prisma) => prisma.user);
|
|
1335
|
-
* }
|
|
1336
|
-
*
|
|
1337
|
-
* // Short syntax
|
|
1338
|
-
* constructor(database: PrimaryDatabaseService) {
|
|
1339
|
-
* super(database, (p) => p.user);
|
|
1340
|
-
* }
|
|
1353
|
+
* import { users } from '@/db/schema';
|
|
1341
1354
|
*
|
|
1342
|
-
* // Complex model names
|
|
1343
1355
|
* constructor(database: PrimaryDatabaseService) {
|
|
1344
|
-
* super(database,
|
|
1356
|
+
* super(database, users);
|
|
1345
1357
|
* }
|
|
1346
1358
|
* ```
|
|
1347
1359
|
*/
|
|
1348
|
-
constructor(database,
|
|
1360
|
+
constructor(database, table) {
|
|
1349
1361
|
this.database = database;
|
|
1362
|
+
this.table = table;
|
|
1363
|
+
this.tableName = (0, import_drizzle_orm2.getTableName)(table);
|
|
1350
1364
|
this.logger = new import_common11.Logger(this.constructor.name);
|
|
1351
|
-
this.modelGetter = getModel;
|
|
1352
1365
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1353
1366
|
}
|
|
1354
1367
|
/**
|
|
@@ -1361,21 +1374,20 @@ var PrimaryBaseRepository = class {
|
|
|
1361
1374
|
* ```typescript
|
|
1362
1375
|
* const user = await userRepository.create({
|
|
1363
1376
|
* email: 'user@example.com',
|
|
1364
|
-
*
|
|
1377
|
+
* firstName: 'John'
|
|
1365
1378
|
* });
|
|
1366
1379
|
* ```
|
|
1367
1380
|
*/
|
|
1368
1381
|
async create(data) {
|
|
1369
1382
|
this.logger.log("Creating record");
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
});
|
|
1383
|
+
const results = await this.db.insert(this.table).values(data).returning();
|
|
1384
|
+
return results[0];
|
|
1373
1385
|
}
|
|
1374
1386
|
/**
|
|
1375
1387
|
* Find a single record by ID
|
|
1376
1388
|
*
|
|
1377
1389
|
* @param id - The record ID
|
|
1378
|
-
* @returns Promise resolving to the record or
|
|
1390
|
+
* @returns Promise resolving to the record or undefined if not found
|
|
1379
1391
|
*
|
|
1380
1392
|
* @example
|
|
1381
1393
|
* ```typescript
|
|
@@ -1384,59 +1396,54 @@ var PrimaryBaseRepository = class {
|
|
|
1384
1396
|
*/
|
|
1385
1397
|
async findById(id) {
|
|
1386
1398
|
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
}
|
|
1399
|
+
const idColumn = this.table.id;
|
|
1400
|
+
return this.model.findFirst({
|
|
1401
|
+
where: (0, import_drizzle_orm2.eq)(idColumn, id)
|
|
1391
1402
|
});
|
|
1392
1403
|
}
|
|
1393
1404
|
/**
|
|
1394
1405
|
* Find a single record with custom where clause
|
|
1395
1406
|
*
|
|
1396
|
-
* @param where -
|
|
1397
|
-
* @returns Promise resolving to the record or
|
|
1407
|
+
* @param where - SQL condition
|
|
1408
|
+
* @returns Promise resolving to the record or undefined if not found
|
|
1398
1409
|
*
|
|
1399
1410
|
* @example
|
|
1400
1411
|
* ```typescript
|
|
1401
|
-
*
|
|
1402
|
-
* const user = await userRepository.findOne(
|
|
1403
|
-
*
|
|
1404
|
-
* // With include
|
|
1405
|
-
* const user = await userRepository.findOne({
|
|
1406
|
-
* where: { email: 'user@example.com' },
|
|
1407
|
-
* include: { posts: true }
|
|
1408
|
-
* });
|
|
1412
|
+
* import { eq } from 'drizzle-orm';
|
|
1413
|
+
* const user = await userRepository.findOne(eq(users.email, 'user@example.com'));
|
|
1409
1414
|
* ```
|
|
1410
1415
|
*/
|
|
1411
1416
|
async findOne(where) {
|
|
1412
1417
|
this.logger.debug("Finding record with custom query");
|
|
1413
|
-
return
|
|
1418
|
+
return this.model.findFirst({
|
|
1414
1419
|
where
|
|
1415
1420
|
});
|
|
1416
1421
|
}
|
|
1417
1422
|
/**
|
|
1418
1423
|
* Find multiple records
|
|
1419
1424
|
*
|
|
1420
|
-
* @param
|
|
1425
|
+
* @param options - Query options (where, orderBy, limit, offset)
|
|
1421
1426
|
* @returns Promise resolving to an array of records
|
|
1422
1427
|
*
|
|
1423
1428
|
* @example
|
|
1424
1429
|
* ```typescript
|
|
1430
|
+
* import { eq, desc } from 'drizzle-orm';
|
|
1431
|
+
*
|
|
1425
1432
|
* // Find all users
|
|
1426
1433
|
* const users = await userRepository.findMany();
|
|
1427
1434
|
*
|
|
1428
1435
|
* // Find with filtering and pagination
|
|
1429
1436
|
* const users = await userRepository.findMany({
|
|
1430
|
-
* where:
|
|
1431
|
-
* orderBy:
|
|
1432
|
-
*
|
|
1433
|
-
*
|
|
1437
|
+
* where: eq(users.accountStatus, 'ACTIVE'),
|
|
1438
|
+
* orderBy: desc(users.createdAt),
|
|
1439
|
+
* limit: 10,
|
|
1440
|
+
* offset: 0
|
|
1434
1441
|
* });
|
|
1435
1442
|
* ```
|
|
1436
1443
|
*/
|
|
1437
|
-
async findMany(
|
|
1444
|
+
async findMany(options) {
|
|
1438
1445
|
this.logger.debug("Finding multiple records");
|
|
1439
|
-
return
|
|
1446
|
+
return this.model.findMany(options);
|
|
1440
1447
|
}
|
|
1441
1448
|
/**
|
|
1442
1449
|
* Update a record by ID
|
|
@@ -1448,41 +1455,40 @@ var PrimaryBaseRepository = class {
|
|
|
1448
1455
|
* @example
|
|
1449
1456
|
* ```typescript
|
|
1450
1457
|
* const user = await userRepository.update('user-id-123', {
|
|
1451
|
-
*
|
|
1458
|
+
* firstName: 'Jane'
|
|
1452
1459
|
* });
|
|
1453
1460
|
* ```
|
|
1454
1461
|
*/
|
|
1455
1462
|
async update(id, data) {
|
|
1456
1463
|
this.logger.log(`Updating record with ID: ${id}`);
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
},
|
|
1461
|
-
data
|
|
1462
|
-
});
|
|
1464
|
+
const idColumn = this.table.id;
|
|
1465
|
+
const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
|
|
1466
|
+
return results[0];
|
|
1463
1467
|
}
|
|
1464
1468
|
/**
|
|
1465
1469
|
* Update multiple records
|
|
1466
1470
|
*
|
|
1467
|
-
* @param where -
|
|
1471
|
+
* @param where - SQL condition to match records
|
|
1468
1472
|
* @param data - The data to update
|
|
1469
1473
|
* @returns Promise resolving to the count of updated records
|
|
1470
1474
|
*
|
|
1471
1475
|
* @example
|
|
1472
1476
|
* ```typescript
|
|
1477
|
+
* import { eq } from 'drizzle-orm';
|
|
1478
|
+
*
|
|
1473
1479
|
* const result = await userRepository.updateMany(
|
|
1474
|
-
*
|
|
1475
|
-
* {
|
|
1480
|
+
* eq(users.accountStatus, 'PENDING'),
|
|
1481
|
+
* { accountStatus: 'ACTIVE' }
|
|
1476
1482
|
* );
|
|
1477
1483
|
* console.log(`Updated ${result.count} users`);
|
|
1478
1484
|
* ```
|
|
1479
1485
|
*/
|
|
1480
1486
|
async updateMany(where, data) {
|
|
1481
1487
|
this.logger.log("Updating multiple records");
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
}
|
|
1488
|
+
const result = await this.db.update(this.table).set(data).where(where);
|
|
1489
|
+
return {
|
|
1490
|
+
count: result.rowCount ?? 0
|
|
1491
|
+
};
|
|
1486
1492
|
}
|
|
1487
1493
|
/**
|
|
1488
1494
|
* Delete a record by ID
|
|
@@ -1497,127 +1503,143 @@ var PrimaryBaseRepository = class {
|
|
|
1497
1503
|
*/
|
|
1498
1504
|
async delete(id) {
|
|
1499
1505
|
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
}
|
|
1504
|
-
});
|
|
1506
|
+
const idColumn = this.table.id;
|
|
1507
|
+
const results = await this.db.delete(this.table).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
|
|
1508
|
+
return results[0];
|
|
1505
1509
|
}
|
|
1506
1510
|
/**
|
|
1507
1511
|
* Delete multiple records
|
|
1508
1512
|
*
|
|
1509
|
-
* @param where -
|
|
1513
|
+
* @param where - SQL condition to match records
|
|
1510
1514
|
* @returns Promise resolving to the count of deleted records
|
|
1511
1515
|
*
|
|
1512
1516
|
* @example
|
|
1513
1517
|
* ```typescript
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
*
|
|
1517
|
-
*
|
|
1518
|
+
* import { lt } from 'drizzle-orm';
|
|
1519
|
+
*
|
|
1520
|
+
* const result = await userRepository.deleteMany(
|
|
1521
|
+
* lt(users.createdAt, new Date('2020-01-01'))
|
|
1522
|
+
* );
|
|
1518
1523
|
* console.log(`Deleted ${result.count} users`);
|
|
1519
1524
|
* ```
|
|
1520
1525
|
*/
|
|
1521
1526
|
async deleteMany(where) {
|
|
1522
1527
|
this.logger.log("Deleting multiple records");
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1528
|
+
const result = await this.db.delete(this.table).where(where);
|
|
1529
|
+
return {
|
|
1530
|
+
count: result.rowCount ?? 0
|
|
1531
|
+
};
|
|
1526
1532
|
}
|
|
1527
1533
|
/**
|
|
1528
1534
|
* Count records
|
|
1529
1535
|
*
|
|
1530
|
-
* @param where - Optional
|
|
1536
|
+
* @param where - Optional SQL condition to filter records
|
|
1531
1537
|
* @returns Promise resolving to the count of records
|
|
1532
1538
|
*
|
|
1533
1539
|
* @example
|
|
1534
1540
|
* ```typescript
|
|
1541
|
+
* import { eq } from 'drizzle-orm';
|
|
1542
|
+
*
|
|
1535
1543
|
* // Count all users
|
|
1536
1544
|
* const total = await userRepository.count();
|
|
1537
1545
|
*
|
|
1538
1546
|
* // Count active users
|
|
1539
|
-
* const activeCount = await userRepository.count(
|
|
1547
|
+
* const activeCount = await userRepository.count(
|
|
1548
|
+
* eq(users.accountStatus, 'ACTIVE')
|
|
1549
|
+
* );
|
|
1540
1550
|
* ```
|
|
1541
1551
|
*/
|
|
1542
1552
|
async count(where) {
|
|
1543
1553
|
this.logger.debug("Counting records");
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
});
|
|
1554
|
+
let query = this.db.select({
|
|
1555
|
+
count: import_drizzle_orm2.sql`count(*)::int`
|
|
1556
|
+
}).from(this.table).$dynamic();
|
|
1557
|
+
if (where) {
|
|
1558
|
+
query = query.where(where);
|
|
1559
|
+
}
|
|
1560
|
+
const results = await query;
|
|
1561
|
+
return results[0].count;
|
|
1547
1562
|
}
|
|
1548
1563
|
/**
|
|
1549
1564
|
* Check if a record exists
|
|
1550
1565
|
*
|
|
1551
|
-
* @param where -
|
|
1566
|
+
* @param where - SQL condition to match records
|
|
1552
1567
|
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1553
1568
|
*
|
|
1554
1569
|
* @example
|
|
1555
1570
|
* ```typescript
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
1558
|
-
*
|
|
1571
|
+
* import { eq } from 'drizzle-orm';
|
|
1572
|
+
*
|
|
1573
|
+
* const emailExists = await userRepository.exists(
|
|
1574
|
+
* eq(users.email, 'user@example.com')
|
|
1575
|
+
* );
|
|
1559
1576
|
* ```
|
|
1560
1577
|
*/
|
|
1561
1578
|
async exists(where) {
|
|
1562
|
-
const count = await this.
|
|
1563
|
-
where
|
|
1564
|
-
});
|
|
1579
|
+
const count = await this.count(where);
|
|
1565
1580
|
return count > 0;
|
|
1566
1581
|
}
|
|
1567
1582
|
};
|
|
1568
1583
|
|
|
1569
1584
|
// src/database/repositories/tenant-base.repository.ts
|
|
1570
1585
|
var import_common12 = require("@nestjs/common");
|
|
1586
|
+
var import_drizzle_orm3 = require("drizzle-orm");
|
|
1571
1587
|
var TenantBaseRepository = class {
|
|
1572
1588
|
static {
|
|
1573
1589
|
__name(this, "TenantBaseRepository");
|
|
1574
1590
|
}
|
|
1575
1591
|
database;
|
|
1592
|
+
table;
|
|
1576
1593
|
logger;
|
|
1577
|
-
modelGetter;
|
|
1578
1594
|
/**
|
|
1579
|
-
*
|
|
1595
|
+
* The table name extracted from the Drizzle table at runtime.
|
|
1596
|
+
* Used to access the query API for this repository's table.
|
|
1597
|
+
*/
|
|
1598
|
+
tableName;
|
|
1599
|
+
/**
|
|
1600
|
+
* Lazy getter for Drizzle client.
|
|
1580
1601
|
* Accesses the client from the database service only when needed,
|
|
1581
1602
|
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1582
1603
|
*/
|
|
1583
|
-
get
|
|
1584
|
-
return this.database.
|
|
1604
|
+
get db() {
|
|
1605
|
+
return this.database.drizzleClient;
|
|
1585
1606
|
}
|
|
1586
1607
|
/**
|
|
1587
|
-
*
|
|
1588
|
-
*
|
|
1608
|
+
* Model query API for THIS repository's table (Prisma-like syntax)
|
|
1609
|
+
* Scoped to only the table this repository manages
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```typescript
|
|
1613
|
+
* // Use relational queries with type safety
|
|
1614
|
+
* const product = await this.model.findFirst({
|
|
1615
|
+
* where: eq(products.id, id),
|
|
1616
|
+
* with: { category: true, variants: true }
|
|
1617
|
+
* });
|
|
1618
|
+
* ```
|
|
1589
1619
|
*/
|
|
1590
1620
|
get model() {
|
|
1591
|
-
return this.
|
|
1621
|
+
return this.database.drizzleClient.query[this.tableName];
|
|
1592
1622
|
}
|
|
1593
1623
|
/**
|
|
1594
1624
|
* Create a new repository instance
|
|
1595
1625
|
*
|
|
1596
1626
|
* @param database - The tenant database service
|
|
1597
|
-
* @param
|
|
1627
|
+
* @param table - The Drizzle table schema object
|
|
1598
1628
|
*
|
|
1599
1629
|
* @example
|
|
1600
1630
|
* ```typescript
|
|
1601
|
-
*
|
|
1602
|
-
* constructor(database: TenantDatabaseService) {
|
|
1603
|
-
* super(database, (prisma) => prisma.product);
|
|
1604
|
-
* }
|
|
1631
|
+
* import { products } from '@/db/schema';
|
|
1605
1632
|
*
|
|
1606
|
-
* // Short syntax
|
|
1607
1633
|
* constructor(database: TenantDatabaseService) {
|
|
1608
|
-
* super(database,
|
|
1609
|
-
* }
|
|
1610
|
-
*
|
|
1611
|
-
* // Complex model names
|
|
1612
|
-
* constructor(database: TenantDatabaseService) {
|
|
1613
|
-
* super(database, (p) => p.inventoryItem);
|
|
1634
|
+
* super(database, products);
|
|
1614
1635
|
* }
|
|
1615
1636
|
* ```
|
|
1616
1637
|
*/
|
|
1617
|
-
constructor(database,
|
|
1638
|
+
constructor(database, table) {
|
|
1618
1639
|
this.database = database;
|
|
1640
|
+
this.table = table;
|
|
1641
|
+
this.tableName = (0, import_drizzle_orm3.getTableName)(table);
|
|
1619
1642
|
this.logger = new import_common12.Logger(this.constructor.name);
|
|
1620
|
-
this.modelGetter = getModel;
|
|
1621
1643
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1622
1644
|
}
|
|
1623
1645
|
/**
|
|
@@ -1637,9 +1659,8 @@ var TenantBaseRepository = class {
|
|
|
1637
1659
|
*/
|
|
1638
1660
|
async create(data) {
|
|
1639
1661
|
this.logger.log("Creating record");
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
});
|
|
1662
|
+
const results = await this.db.insert(this.table).values(data).returning();
|
|
1663
|
+
return results[0];
|
|
1643
1664
|
}
|
|
1644
1665
|
/**
|
|
1645
1666
|
* Find a single record by ID
|
|
@@ -1654,59 +1675,65 @@ var TenantBaseRepository = class {
|
|
|
1654
1675
|
*/
|
|
1655
1676
|
async findById(id) {
|
|
1656
1677
|
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
}
|
|
1661
|
-
});
|
|
1678
|
+
const idColumn = this.table.id;
|
|
1679
|
+
const results = await this.db.select().from(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).limit(1);
|
|
1680
|
+
return results[0] ?? null;
|
|
1662
1681
|
}
|
|
1663
1682
|
/**
|
|
1664
1683
|
* Find a single record with custom where clause
|
|
1665
1684
|
*
|
|
1666
|
-
* @param where -
|
|
1685
|
+
* @param where - SQL condition
|
|
1667
1686
|
* @returns Promise resolving to the record or null if not found
|
|
1668
1687
|
*
|
|
1669
1688
|
* @example
|
|
1670
1689
|
* ```typescript
|
|
1671
|
-
*
|
|
1672
|
-
* const product = await productRepository.findOne(
|
|
1673
|
-
*
|
|
1674
|
-
* // With include
|
|
1675
|
-
* const product = await productRepository.findOne({
|
|
1676
|
-
* where: { sku: 'WDG-001' },
|
|
1677
|
-
* include: { category: true }
|
|
1678
|
-
* });
|
|
1690
|
+
* import { eq } from 'drizzle-orm';
|
|
1691
|
+
* const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
|
|
1679
1692
|
* ```
|
|
1680
1693
|
*/
|
|
1681
1694
|
async findOne(where) {
|
|
1682
1695
|
this.logger.debug("Finding record with custom query");
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
});
|
|
1696
|
+
const results = await this.db.select().from(this.table).where(where).limit(1);
|
|
1697
|
+
return results[0] ?? null;
|
|
1686
1698
|
}
|
|
1687
1699
|
/**
|
|
1688
1700
|
* Find multiple records
|
|
1689
1701
|
*
|
|
1690
|
-
* @param
|
|
1702
|
+
* @param options - Query options (where, orderBy, limit, offset)
|
|
1691
1703
|
* @returns Promise resolving to an array of records
|
|
1692
1704
|
*
|
|
1693
1705
|
* @example
|
|
1694
1706
|
* ```typescript
|
|
1707
|
+
* import { eq, desc } from 'drizzle-orm';
|
|
1708
|
+
*
|
|
1695
1709
|
* // Find all products
|
|
1696
1710
|
* const products = await productRepository.findMany();
|
|
1697
1711
|
*
|
|
1698
1712
|
* // Find with filtering and pagination
|
|
1699
1713
|
* const products = await productRepository.findMany({
|
|
1700
|
-
* where:
|
|
1701
|
-
* orderBy:
|
|
1702
|
-
*
|
|
1703
|
-
*
|
|
1714
|
+
* where: eq(products.status, 'ACTIVE'),
|
|
1715
|
+
* orderBy: desc(products.createdAt),
|
|
1716
|
+
* limit: 10,
|
|
1717
|
+
* offset: 0
|
|
1704
1718
|
* });
|
|
1705
1719
|
* ```
|
|
1706
1720
|
*/
|
|
1707
|
-
async findMany(
|
|
1721
|
+
async findMany(options) {
|
|
1708
1722
|
this.logger.debug("Finding multiple records");
|
|
1709
|
-
|
|
1723
|
+
let query = this.db.select().from(this.table).$dynamic();
|
|
1724
|
+
if (options?.where) {
|
|
1725
|
+
query = query.where(options.where);
|
|
1726
|
+
}
|
|
1727
|
+
if (options?.orderBy) {
|
|
1728
|
+
query = query.orderBy(options.orderBy);
|
|
1729
|
+
}
|
|
1730
|
+
if (options?.limit) {
|
|
1731
|
+
query = query.limit(options.limit);
|
|
1732
|
+
}
|
|
1733
|
+
if (options?.offset) {
|
|
1734
|
+
query = query.offset(options.offset);
|
|
1735
|
+
}
|
|
1736
|
+
return await query;
|
|
1710
1737
|
}
|
|
1711
1738
|
/**
|
|
1712
1739
|
* Update a record by ID
|
|
@@ -1724,24 +1751,23 @@ var TenantBaseRepository = class {
|
|
|
1724
1751
|
*/
|
|
1725
1752
|
async update(id, data) {
|
|
1726
1753
|
this.logger.log(`Updating record with ID: ${id}`);
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
},
|
|
1731
|
-
data
|
|
1732
|
-
});
|
|
1754
|
+
const idColumn = this.table.id;
|
|
1755
|
+
const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
|
|
1756
|
+
return results[0];
|
|
1733
1757
|
}
|
|
1734
1758
|
/**
|
|
1735
1759
|
* Update multiple records
|
|
1736
1760
|
*
|
|
1737
|
-
* @param where -
|
|
1761
|
+
* @param where - SQL condition to match records
|
|
1738
1762
|
* @param data - The data to update
|
|
1739
1763
|
* @returns Promise resolving to the count of updated records
|
|
1740
1764
|
*
|
|
1741
1765
|
* @example
|
|
1742
1766
|
* ```typescript
|
|
1767
|
+
* import { eq } from 'drizzle-orm';
|
|
1768
|
+
*
|
|
1743
1769
|
* const result = await productRepository.updateMany(
|
|
1744
|
-
*
|
|
1770
|
+
* eq(products.status, 'PENDING'),
|
|
1745
1771
|
* { status: 'ACTIVE' }
|
|
1746
1772
|
* );
|
|
1747
1773
|
* console.log(`Updated ${result.count} products`);
|
|
@@ -1749,10 +1775,10 @@ var TenantBaseRepository = class {
|
|
|
1749
1775
|
*/
|
|
1750
1776
|
async updateMany(where, data) {
|
|
1751
1777
|
this.logger.log("Updating multiple records");
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
}
|
|
1778
|
+
const result = await this.db.update(this.table).set(data).where(where);
|
|
1779
|
+
return {
|
|
1780
|
+
count: result.rowCount ?? 0
|
|
1781
|
+
};
|
|
1756
1782
|
}
|
|
1757
1783
|
/**
|
|
1758
1784
|
* Delete a record by ID
|
|
@@ -1767,71 +1793,80 @@ var TenantBaseRepository = class {
|
|
|
1767
1793
|
*/
|
|
1768
1794
|
async delete(id) {
|
|
1769
1795
|
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
}
|
|
1774
|
-
});
|
|
1796
|
+
const idColumn = this.table.id;
|
|
1797
|
+
const results = await this.db.delete(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
|
|
1798
|
+
return results[0];
|
|
1775
1799
|
}
|
|
1776
1800
|
/**
|
|
1777
1801
|
* Delete multiple records
|
|
1778
1802
|
*
|
|
1779
|
-
* @param where -
|
|
1803
|
+
* @param where - SQL condition to match records
|
|
1780
1804
|
* @returns Promise resolving to the count of deleted records
|
|
1781
1805
|
*
|
|
1782
1806
|
* @example
|
|
1783
1807
|
* ```typescript
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
*
|
|
1808
|
+
* import { lt } from 'drizzle-orm';
|
|
1809
|
+
*
|
|
1810
|
+
* const result = await productRepository.deleteMany(
|
|
1811
|
+
* lt(products.createdAt, new Date('2020-01-01'))
|
|
1812
|
+
* );
|
|
1788
1813
|
* console.log(`Deleted ${result.count} products`);
|
|
1789
1814
|
* ```
|
|
1790
1815
|
*/
|
|
1791
1816
|
async deleteMany(where) {
|
|
1792
1817
|
this.logger.log("Deleting multiple records");
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1818
|
+
const result = await this.db.delete(this.table).where(where);
|
|
1819
|
+
return {
|
|
1820
|
+
count: result.rowCount ?? 0
|
|
1821
|
+
};
|
|
1796
1822
|
}
|
|
1797
1823
|
/**
|
|
1798
1824
|
* Count records
|
|
1799
1825
|
*
|
|
1800
|
-
* @param where - Optional
|
|
1826
|
+
* @param where - Optional SQL condition to filter records
|
|
1801
1827
|
* @returns Promise resolving to the count of records
|
|
1802
1828
|
*
|
|
1803
1829
|
* @example
|
|
1804
1830
|
* ```typescript
|
|
1831
|
+
* import { eq } from 'drizzle-orm';
|
|
1832
|
+
*
|
|
1805
1833
|
* // Count all products
|
|
1806
1834
|
* const total = await productRepository.count();
|
|
1807
1835
|
*
|
|
1808
1836
|
* // Count active products
|
|
1809
|
-
* const activeCount = await productRepository.count(
|
|
1837
|
+
* const activeCount = await productRepository.count(
|
|
1838
|
+
* eq(products.status, 'ACTIVE')
|
|
1839
|
+
* );
|
|
1810
1840
|
* ```
|
|
1811
1841
|
*/
|
|
1812
1842
|
async count(where) {
|
|
1813
1843
|
this.logger.debug("Counting records");
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
});
|
|
1844
|
+
let query = this.db.select({
|
|
1845
|
+
count: import_drizzle_orm3.sql`count(*)::int`
|
|
1846
|
+
}).from(this.table).$dynamic();
|
|
1847
|
+
if (where) {
|
|
1848
|
+
query = query.where(where);
|
|
1849
|
+
}
|
|
1850
|
+
const results = await query;
|
|
1851
|
+
return results[0].count;
|
|
1817
1852
|
}
|
|
1818
1853
|
/**
|
|
1819
1854
|
* Check if a record exists
|
|
1820
1855
|
*
|
|
1821
|
-
* @param where -
|
|
1856
|
+
* @param where - SQL condition to match records
|
|
1822
1857
|
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1823
1858
|
*
|
|
1824
1859
|
* @example
|
|
1825
1860
|
* ```typescript
|
|
1826
|
-
*
|
|
1827
|
-
*
|
|
1828
|
-
*
|
|
1861
|
+
* import { eq } from 'drizzle-orm';
|
|
1862
|
+
*
|
|
1863
|
+
* const skuExists = await productRepository.exists(
|
|
1864
|
+
* eq(products.sku, 'WDG-001')
|
|
1865
|
+
* );
|
|
1829
1866
|
* ```
|
|
1830
1867
|
*/
|
|
1831
1868
|
async exists(where) {
|
|
1832
|
-
const count = await this.
|
|
1833
|
-
where
|
|
1834
|
-
});
|
|
1869
|
+
const count = await this.count(where);
|
|
1835
1870
|
return count > 0;
|
|
1836
1871
|
}
|
|
1837
1872
|
};
|
|
@@ -1988,15 +2023,16 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
1988
2023
|
const exceptionResponse = exception.getResponse();
|
|
1989
2024
|
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
1990
2025
|
const responseObj = exceptionResponse;
|
|
1991
|
-
if (
|
|
2026
|
+
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
1992
2027
|
errors = responseObj.errors;
|
|
1993
|
-
detail = responseObj.detail || exception.message;
|
|
1994
|
-
} else if (
|
|
2028
|
+
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2029
|
+
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
1995
2030
|
errors = responseObj.message.map((msg) => {
|
|
1996
|
-
if (typeof msg === "object" &&
|
|
2031
|
+
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2032
|
+
const constraintValues = Object.values(msg.constraints);
|
|
1997
2033
|
return {
|
|
1998
2034
|
field: msg.property,
|
|
1999
|
-
message:
|
|
2035
|
+
message: constraintValues[0] ?? "Validation failed"
|
|
2000
2036
|
};
|
|
2001
2037
|
}
|
|
2002
2038
|
return {
|
|
@@ -2004,13 +2040,14 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
2004
2040
|
};
|
|
2005
2041
|
});
|
|
2006
2042
|
detail = "Validation failed";
|
|
2007
|
-
} else if (responseObj
|
|
2043
|
+
} else if ("message" in responseObj) {
|
|
2044
|
+
const message = responseObj.message;
|
|
2008
2045
|
errors = [
|
|
2009
2046
|
{
|
|
2010
|
-
message: Array.isArray(
|
|
2047
|
+
message: Array.isArray(message) ? message.join(", ") : message
|
|
2011
2048
|
}
|
|
2012
2049
|
];
|
|
2013
|
-
detail = responseObj.error || exception.message;
|
|
2050
|
+
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2014
2051
|
}
|
|
2015
2052
|
} else if (typeof exceptionResponse === "string") {
|
|
2016
2053
|
errors = [
|
|
@@ -3058,14 +3095,15 @@ function getLevelsUpTo(level) {
|
|
|
3058
3095
|
"debug",
|
|
3059
3096
|
"verbose"
|
|
3060
3097
|
];
|
|
3061
|
-
const
|
|
3062
|
-
if (
|
|
3098
|
+
const isValidLevel = /* @__PURE__ */ __name((l) => allLevels.includes(l), "isValidLevel");
|
|
3099
|
+
if (!isValidLevel(level)) {
|
|
3063
3100
|
return [
|
|
3064
3101
|
"error",
|
|
3065
3102
|
"warn",
|
|
3066
3103
|
"log"
|
|
3067
3104
|
];
|
|
3068
3105
|
}
|
|
3106
|
+
const levelIndex = allLevels.indexOf(level);
|
|
3069
3107
|
return allLevels.slice(0, levelIndex + 1);
|
|
3070
3108
|
}
|
|
3071
3109
|
__name(getLevelsUpTo, "getLevelsUpTo");
|