From f0f6970a08984e98806e6a2c038baa2ae76917dd Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 5 Jun 2025 18:25:10 -0700 Subject: [PATCH] Added timer controllers for managing usage stats --- src/controllers/timer-controllers.js | 52 +++++++++++++------ src/use-cases/index.js | 3 ++ src/use-cases/usage-use-cases.js | 24 ++++++--- .../controllers/timer-controllers.unit.js | 37 +++++++++---- test/unit/mocks/use-cases/index.js | 8 +++ 5 files changed, 90 insertions(+), 34 deletions(-) diff --git a/src/controllers/timer-controllers.js b/src/controllers/timer-controllers.js index 385b918..7320529 100644 --- a/src/controllers/timer-controllers.js +++ b/src/controllers/timer-controllers.js @@ -23,58 +23,76 @@ class TimerControllers { this.debugLevel = localConfig.debugLevel + // Constants + this.cleanUsageInterval = 60000 * 60 // 1 hour + this.backupUsageInterval = 60000 * 1 // 1 minute + // Encapsulate dependencies this.config = config // Bind 'this' object to all subfunctions. - this.exampleTimerFunc = this.exampleTimerFunc.bind(this) this.cleanUsage = this.cleanUsage.bind(this) - - // this.startTimers() + this.backupUsage = this.backupUsage.bind(this) } // Start all the time-based controllers. startTimers () { // Any new timer control functions can be added here. They will be started // when the server starts. - this.optimizeWalletHandle = setInterval(this.exampleTimerFunc, 60000 * 60) - this.cleanUsageHandle = setInterval(this.cleanUsage, 60000 * 60) // 1 hour + this.cleanUsageHandle = setInterval(this.cleanUsage, this.cleanUsageInterval) + this.backupUsageHandle = setInterval(this.backupUsage, this.backupUsageInterval) return true } stopTimers () { - clearInterval(this.optimizeWalletHandle) clearInterval(this.cleanUsageHandle) + clearInterval(this.backupUsageHandle) } - // Replace this example function with your own timer handler. - exampleTimerFunc (negativeTest) { + // Clean the usage state so that stats reflect the last 24 hours. + cleanUsage () { try { - console.log('Example timer controller executed.') + clearInterval(this.cleanUsageHandle) - if (negativeTest) throw new Error('test error') + const now = new Date() + console.log(`cleanUsage() Timer Controller executing at ${now.toLocaleString()}`) + + this.useCases.usage.cleanUsage() + + this.cleanUsageHandle = setInterval(this.cleanUsage, this.cleanUsageInterval) return true } catch (err) { - console.error('Error in exampleTimerFunc(): ', err) + console.error('Error in time-controller.js/cleanUsage(): ', err) + + this.cleanUsageHandle = setInterval(this.cleanUsage, this.cleanUsageInterval) // Note: Do not throw an error. This is a top-level function. return false } } - // Clean the usage state so that stats reflect the last 24 hours. - cleanUsage () { + // Backup the usage stats to the database + async backupUsage () { try { - const now = new Date() - console.log(`cleanUsage() Timer Controller executing at ${now.toLocaleString()}`) + clearInterval(this.backupUsageHandle) - this.useCases.usage.cleanUsage() + console.log('backupUsage() Timer Controller executing at ', new Date().toLocaleString()) + + // Clear the database of old usage data. + await this.useCases.usage.clearUsage() + + // Save the current usage snapshot to the database. + await this.useCases.usage.saveUsage() + + this.backupUsageHandle = setInterval(this.backupUsage, this.backupUsageInterval) return true } catch (err) { - console.error('Error in time-controller.js/cleanUsage(): ', err) + console.error('Error in time-controller.js/backupUsage(): ', err) + + this.backupUsageHandle = setInterval(this.backupUsage, this.backupUsageInterval) // Note: Do not throw an error. This is a top-level function. return false diff --git a/src/use-cases/index.js b/src/use-cases/index.js index 8ce82b3..dcbe301 100644 --- a/src/use-cases/index.js +++ b/src/use-cases/index.js @@ -24,6 +24,9 @@ class UseCases { // Run any startup Use Cases at the start of the app. async start () { + // Load the usage stats from the database + await this.usage.loadUsage() + console.log('Async Use Cases have been started.') return true diff --git a/src/use-cases/usage-use-cases.js b/src/use-cases/usage-use-cases.js index d177005..c6886c2 100644 --- a/src/use-cases/usage-use-cases.js +++ b/src/use-cases/usage-use-cases.js @@ -23,6 +23,9 @@ class UsageUseCases { ) } + // Encapsulate dependencies + this.UsageModel = this.adapters.localdb.Usage + // Bind 'this' object to all subfunctions this.cleanUsage = this.cleanUsage.bind(this) this.getRestSummary = this.getRestSummary.bind(this) @@ -119,12 +122,12 @@ class UsageUseCases { // Clear the usage database data async clearUsage () { try { - await this.adapters.Usage.deleteMany({}) + await this.UsageModel.deleteMany({}) // Debugging: verify the database is empty // Delete this code after debugging - const usage = await this.adapters.Usage.find({}) - console.log('usage: ', usage) + const usage = await this.UsageModel.find({}) + console.log('clearUsage() usage: ', usage) } catch (err) { console.error('Error in usage-use-cases.js/clearUsage()') throw err @@ -142,7 +145,14 @@ class UsageUseCases { console.log('saveUsage() thisRestCall: ', thisRestCall) } - const usage = new this.UsageModel(thisRestCall) + const usageData = { + ip: thisRestCall.ip, + url: thisRestCall.url, + method: thisRestCall.method, + timestamp: thisRestCall.timestamp + } + + const usage = new this.UsageModel(usageData) await usage.save() } } catch (err) { @@ -154,7 +164,7 @@ class UsageUseCases { // Load usage data from the database async loadUsage () { try { - const usage = await this.adapters.Usage.find({}) + const usage = await this.UsageModel.find({}) // console.log('usage: ', usage) if (usage[5]) { @@ -163,8 +173,8 @@ class UsageUseCases { restCalls = usage } catch (err) { - console.error('Error in usage-use-cases.js/loadUsage()') - throw err + console.error('Error in usage-use-cases.js/loadUsage(): ', err) + // throw err } } } diff --git a/test/unit/controllers/timer-controllers.unit.js b/test/unit/controllers/timer-controllers.unit.js index fda1b72..3daacdc 100644 --- a/test/unit/controllers/timer-controllers.unit.js +++ b/test/unit/controllers/timer-controllers.unit.js @@ -66,19 +66,19 @@ describe('#Timer-Controllers', () => { }) }) - describe('#exampleTimerFunc', () => { - it('should kick off the Use Case', async () => { - const result = await uut.exampleTimerFunc() + // describe('#exampleTimerFunc', () => { + // it('should kick off the Use Case', async () => { + // const result = await uut.exampleTimerFunc() - assert.equal(result, true) - }) + // assert.equal(result, true) + // }) - it('should return false on error', async () => { - const result = await uut.exampleTimerFunc(true) + // it('should return false on error', async () => { + // const result = await uut.exampleTimerFunc(true) - assert.equal(result, false) - }) - }) + // assert.equal(result, false) + // }) + // }) describe('#cleanUsage', () => { it('should kick off the Use Case', async () => { @@ -94,4 +94,21 @@ describe('#Timer-Controllers', () => { assert.equal(result, false) }) }) + + describe('#backupUsage', () => { + it('should kick off the Use Case', async () => { + const result = await uut.backupUsage() + + assert.equal(result, true) + }) + + it('should return false on error', async () => { + sandbox.stub(uut.useCases.usage, 'clearUsage').throws(new Error('test error')) + // sandbox.stub(uut.useCases.usage, 'saveUsage').throws(new Error('test error')) + + const result = await uut.backupUsage() + + assert.equal(result, false) + }) + }) }) diff --git a/test/unit/mocks/use-cases/index.js b/test/unit/mocks/use-cases/index.js index 73979c3..a915fa5 100644 --- a/test/unit/mocks/use-cases/index.js +++ b/test/unit/mocks/use-cases/index.js @@ -47,6 +47,14 @@ class UsageUseCaseMock { async getTopEndpoints(existingUser, newData) { return true } + + async clearUsage() { + return true + } + + async saveUsage() { + return true + } } class UseCasesMock {