velocious 1.0.478 → 1.0.480
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 +3 -3
- package/build/background-jobs/scheduler.js +2 -2
- package/build/configuration-types.js +1 -1
- package/build/database/record/index.js +159 -2
- package/build/src/background-jobs/scheduler.js +3 -3
- package/build/src/configuration-types.d.ts +8 -5
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/database/record/index.d.ts +44 -1
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +131 -3
- package/package.json +1 -1
- package/src/background-jobs/scheduler.js +2 -2
- package/src/configuration-types.js +1 -1
- package/src/database/record/index.js +159 -2
package/package.json
CHANGED
|
@@ -169,7 +169,7 @@ export default class BackgroundJobsScheduler {
|
|
|
169
169
|
const everyConfig = /** @type {NonNullable<typeof jobConfiguration.every>} */ (jobConfiguration.every)
|
|
170
170
|
const {everyValue, firstInValue} = this.normalizeEvery(everyConfig)
|
|
171
171
|
const intervalMs = parseScheduledDuration(everyValue, `${jobKey}.every`)
|
|
172
|
-
const firstInMs = firstInValue !== undefined ? parseScheduledDuration(firstInValue, `${jobKey}.
|
|
172
|
+
const firstInMs = firstInValue !== undefined ? parseScheduledDuration(firstInValue, `${jobKey}.firstIn`) : intervalMs
|
|
173
173
|
|
|
174
174
|
if (intervalMs < 1) {
|
|
175
175
|
throw new Error(`Scheduled background job ${jobKey}.every must be at least 1 millisecond.`)
|
|
@@ -262,7 +262,7 @@ export default class BackgroundJobsScheduler {
|
|
|
262
262
|
return {everyValue}
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
return {everyValue, firstInValue: everyOptions.firstIn
|
|
265
|
+
return {everyValue, firstInValue: everyOptions.firstIn}
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
return {everyValue: every}
|
|
@@ -191,7 +191,6 @@
|
|
|
191
191
|
/**
|
|
192
192
|
* @typedef {object} ScheduledBackgroundJobEveryOptions
|
|
193
193
|
* @property {number | string} [firstIn] - Delay before the first enqueue.
|
|
194
|
-
* @property {number | string} [first_in] - Sidekiq-style alias for `firstIn`.
|
|
195
194
|
*/
|
|
196
195
|
|
|
197
196
|
/**
|
|
@@ -476,6 +475,7 @@
|
|
|
476
475
|
/**
|
|
477
476
|
* @typedef {object} TenantDatabaseProviderType
|
|
478
477
|
* @property {function({configuration: import("./configuration.js").default, identifier: string}) : Array<?> | Promise<Array<?>>} listTenants - Lists tenants that should be created, checked, or migrated for this database identifier.
|
|
478
|
+
* @property {function({configuration: import("./configuration.js").default, identifier: string}) : Array<?> | Promise<Array<?>>} [listRestrictTenants] - Lists existing tenants that should be checked for dependent restrict destroys. Defaults to listTenants.
|
|
479
479
|
* @property {function({configuration: import("./configuration.js").default, databaseConfiguration: DatabaseConfigurationType, identifier: string, tenant: ?}) : void | Promise<void>} [createDatabase] - Creates the tenant database/schema for one tenant.
|
|
480
480
|
* @property {function({configuration: import("./configuration.js").default, databaseConfiguration: DatabaseConfigurationType, identifier: string, tenant: ?}) : void | Promise<void>} [dropDatabase] - Drops the tenant database/schema for one tenant.
|
|
481
481
|
* @property {function({configuration: import("./configuration.js").default, databaseConfiguration: DatabaseConfigurationType, identifier: string, tenant: ?}) : void | Promise<void>} [checkTenant] - Checks one tenant database before generic connection validation.
|
|
@@ -17,6 +17,13 @@
|
|
|
17
17
|
* @typedef {{new (changes?: Record<string, unknown>): T}} ModelConstructor
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* RestrictInstanceRelationship type.
|
|
22
|
+
* @typedef {import("./instance-relationships/base.js").default & {query: () => ModelClassQuery<typeof VelociousDatabaseRecord>}} RestrictInstanceRelationship
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** @typedef {import("../../configuration-types.js").TenantDatabaseProviderType} TenantDatabaseProviderType */
|
|
26
|
+
|
|
20
27
|
import timeout from "awaitery/build/timeout.js"
|
|
21
28
|
import BelongsToInstanceRelationship from "./instance-relationships/belongs-to.js"
|
|
22
29
|
import BelongsToRelationship from "./relationships/belongs-to.js"
|
|
@@ -1628,6 +1635,14 @@ class VelociousDatabaseRecord {
|
|
|
1628
1635
|
this._tenantDatabaseIdentifierResolver = databaseIdentifierOrResolver
|
|
1629
1636
|
}
|
|
1630
1637
|
|
|
1638
|
+
/**
|
|
1639
|
+
* Runs has tenant database identifier resolver.
|
|
1640
|
+
* @returns {boolean} - Whether this model resolves its database from the current tenant.
|
|
1641
|
+
*/
|
|
1642
|
+
static hasTenantDatabaseIdentifierResolver() {
|
|
1643
|
+
return Boolean(this._tenantDatabaseIdentifierResolver)
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1631
1646
|
/**
|
|
1632
1647
|
* Runs get tenant database identifier.
|
|
1633
1648
|
* @param {?} [tenant] - Tenant override.
|
|
@@ -3359,6 +3374,148 @@ class VelociousDatabaseRecord {
|
|
|
3359
3374
|
return this.getModelClass().connection()
|
|
3360
3375
|
}
|
|
3361
3376
|
|
|
3377
|
+
/**
|
|
3378
|
+
* Counts dependent records for a `dependent: "restrict"` relationship.
|
|
3379
|
+
* @param {RestrictInstanceRelationship} instanceRelationship - Relationship instance to count.
|
|
3380
|
+
* @returns {Promise<number>} - Dependent row count.
|
|
3381
|
+
*/
|
|
3382
|
+
async _dependentRestrictCount(instanceRelationship) {
|
|
3383
|
+
const TargetModelClass = instanceRelationship.getTargetModelClass()
|
|
3384
|
+
|
|
3385
|
+
if (!TargetModelClass || !TargetModelClass.hasTenantDatabaseIdentifierResolver()) {
|
|
3386
|
+
return await instanceRelationship.query().count()
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
if (this.getModelClass().hasTenantDatabaseIdentifierResolver()) {
|
|
3390
|
+
return await instanceRelationship.query().count()
|
|
3391
|
+
}
|
|
3392
|
+
|
|
3393
|
+
return await this._dependentRestrictTenantCount(instanceRelationship, TargetModelClass)
|
|
3394
|
+
}
|
|
3395
|
+
|
|
3396
|
+
/**
|
|
3397
|
+
* Counts tenant-scoped dependent records across all provider-listed tenants.
|
|
3398
|
+
* @param {RestrictInstanceRelationship} instanceRelationship - Relationship instance to count.
|
|
3399
|
+
* @param {typeof VelociousDatabaseRecord} TargetModelClass - Related model class.
|
|
3400
|
+
* @returns {Promise<number>} - Dependent row count.
|
|
3401
|
+
*/
|
|
3402
|
+
async _dependentRestrictTenantCount(instanceRelationship, TargetModelClass) {
|
|
3403
|
+
const configuration = this.getModelClass()._getConfiguration()
|
|
3404
|
+
const tenantDatabaseProviders = configuration.getTenantDatabaseProviders()
|
|
3405
|
+
const providerEntries = Object.entries(tenantDatabaseProviders)
|
|
3406
|
+
const targetIdentifier = TargetModelClass.getTenantDatabaseIdentifier(null)
|
|
3407
|
+
|
|
3408
|
+
if (providerEntries.length == 0) {
|
|
3409
|
+
throw new Error(`Cannot check dependent ${instanceRelationship.getRelationship().getRelationshipName()} because ${TargetModelClass.getModelName()} switches tenant databases but no tenant database providers are configured`)
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
if (targetIdentifier) {
|
|
3413
|
+
const provider = tenantDatabaseProviders[targetIdentifier]
|
|
3414
|
+
|
|
3415
|
+
if (!provider) {
|
|
3416
|
+
throw new Error(`Cannot check dependent ${instanceRelationship.getRelationship().getRelationshipName()} because ${TargetModelClass.getModelName()} switches tenant database ${targetIdentifier} but no tenant database provider is configured for ${targetIdentifier}`)
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
return await this._dependentRestrictProviderCount(instanceRelationship, TargetModelClass, targetIdentifier, provider)
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
let matchingProviderSeen = false
|
|
3423
|
+
|
|
3424
|
+
for (const [identifier, provider] of providerEntries) {
|
|
3425
|
+
const tenants = await this._dependentRestrictProviderTenants(instanceRelationship, TargetModelClass, identifier, provider)
|
|
3426
|
+
|
|
3427
|
+
for (const tenant of tenants) {
|
|
3428
|
+
if (TargetModelClass.getTenantDatabaseIdentifier(tenant) != identifier) {
|
|
3429
|
+
continue
|
|
3430
|
+
}
|
|
3431
|
+
|
|
3432
|
+
matchingProviderSeen = true
|
|
3433
|
+
|
|
3434
|
+
const count = await configuration.runWithTenant(tenant, async () => {
|
|
3435
|
+
if (!configuration.isDatabaseIdentifierActive(identifier)) {
|
|
3436
|
+
throw new Error(`Tenant database identifier ${identifier} is inactive while checking dependent ${instanceRelationship.getRelationship().getRelationshipName()}`)
|
|
3437
|
+
}
|
|
3438
|
+
|
|
3439
|
+
return await configuration.ensureConnections({name: `Dependent restrict count: ${TargetModelClass.getModelName()}`}, async () => {
|
|
3440
|
+
return await instanceRelationship.query().count()
|
|
3441
|
+
})
|
|
3442
|
+
})
|
|
3443
|
+
|
|
3444
|
+
if (count > 0) return count
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3448
|
+
if (!matchingProviderSeen) {
|
|
3449
|
+
throw new Error(`Cannot check dependent ${instanceRelationship.getRelationship().getRelationshipName()} because no tenant database provider matched ${TargetModelClass.getModelName()}`)
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
return 0
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
/**
|
|
3456
|
+
* Counts tenant-scoped dependent records for one configured tenant provider.
|
|
3457
|
+
* @param {RestrictInstanceRelationship} instanceRelationship - Relationship instance to count.
|
|
3458
|
+
* @param {typeof VelociousDatabaseRecord} TargetModelClass - Related model class.
|
|
3459
|
+
* @param {string} identifier - Tenant database identifier.
|
|
3460
|
+
* @param {TenantDatabaseProviderType} provider - Tenant database provider.
|
|
3461
|
+
* @returns {Promise<number>} - Dependent row count.
|
|
3462
|
+
*/
|
|
3463
|
+
async _dependentRestrictProviderCount(instanceRelationship, TargetModelClass, identifier, provider) {
|
|
3464
|
+
const configuration = this.getModelClass()._getConfiguration()
|
|
3465
|
+
const tenants = await this._dependentRestrictProviderTenants(instanceRelationship, TargetModelClass, identifier, provider)
|
|
3466
|
+
|
|
3467
|
+
for (const tenant of tenants) {
|
|
3468
|
+
const count = await configuration.runWithTenant(tenant, async () => {
|
|
3469
|
+
if (!configuration.isDatabaseIdentifierActive(identifier)) {
|
|
3470
|
+
throw new Error(`Tenant database identifier ${identifier} is inactive while checking dependent ${instanceRelationship.getRelationship().getRelationshipName()}`)
|
|
3471
|
+
}
|
|
3472
|
+
|
|
3473
|
+
return await configuration.ensureConnections({name: `Dependent restrict count: ${TargetModelClass.getModelName()}`}, async () => {
|
|
3474
|
+
return await instanceRelationship.query().count()
|
|
3475
|
+
})
|
|
3476
|
+
})
|
|
3477
|
+
|
|
3478
|
+
if (count > 0) return count
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3481
|
+
return 0
|
|
3482
|
+
}
|
|
3483
|
+
|
|
3484
|
+
/**
|
|
3485
|
+
* Lists restrict-check tenants for one configured tenant provider.
|
|
3486
|
+
* @param {RestrictInstanceRelationship} instanceRelationship - Relationship instance to count.
|
|
3487
|
+
* @param {typeof VelociousDatabaseRecord} TargetModelClass - Related model class.
|
|
3488
|
+
* @param {string} identifier - Tenant database identifier.
|
|
3489
|
+
* @param {TenantDatabaseProviderType} provider - Tenant database provider.
|
|
3490
|
+
* @returns {Promise<Array<?>>} - Listed tenant objects.
|
|
3491
|
+
*/
|
|
3492
|
+
async _dependentRestrictProviderTenants(instanceRelationship, TargetModelClass, identifier, provider) {
|
|
3493
|
+
const configuration = this.getModelClass()._getConfiguration()
|
|
3494
|
+
const listTenants = typeof provider.listRestrictTenants == "function"
|
|
3495
|
+
? provider.listRestrictTenants
|
|
3496
|
+
: provider.listTenants
|
|
3497
|
+
const listTenantsMethodName = typeof provider.listRestrictTenants == "function"
|
|
3498
|
+
? "listRestrictTenants"
|
|
3499
|
+
: "listTenants"
|
|
3500
|
+
|
|
3501
|
+
if (typeof listTenants != "function") {
|
|
3502
|
+
throw new Error(`Tenant database provider for ${identifier} must define listTenants or listRestrictTenants before dependent restrict can check ${instanceRelationship.getRelationship().getRelationshipName()}`)
|
|
3503
|
+
}
|
|
3504
|
+
|
|
3505
|
+
const tenants = await configuration.ensureConnections({name: `Dependent restrict tenants: ${TargetModelClass.getModelName()}`}, async () => {
|
|
3506
|
+
return await listTenants({
|
|
3507
|
+
configuration,
|
|
3508
|
+
identifier
|
|
3509
|
+
})
|
|
3510
|
+
})
|
|
3511
|
+
|
|
3512
|
+
if (!Array.isArray(tenants)) {
|
|
3513
|
+
throw new Error(`Tenant database provider for ${identifier} must return an array from ${listTenantsMethodName}`)
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
return tenants
|
|
3517
|
+
}
|
|
3518
|
+
|
|
3362
3519
|
/**
|
|
3363
3520
|
* Destroys the record in the database and all of its dependent records.
|
|
3364
3521
|
* @returns {Promise<void>} - Resolves when complete.
|
|
@@ -3368,8 +3525,8 @@ class VelociousDatabaseRecord {
|
|
|
3368
3525
|
|
|
3369
3526
|
for (const relationship of this.getModelClass().getRelationships()) {
|
|
3370
3527
|
if (relationship.getDependent() == "restrict") {
|
|
3371
|
-
const instanceRelationship = /** @type {
|
|
3372
|
-
const count = await
|
|
3528
|
+
const instanceRelationship = /** @type {RestrictInstanceRelationship} */ (this.getRelationshipByName(relationship.getRelationshipName()))
|
|
3529
|
+
const count = await this._dependentRestrictCount(instanceRelationship)
|
|
3373
3530
|
|
|
3374
3531
|
if (count > 0) {
|
|
3375
3532
|
throw new Error(`Cannot delete record because dependent ${relationship.getRelationshipName()} exist`)
|