mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(core): Check license config for insights max retention (#15256)
This commit is contained in:
committed by
GitHub
parent
15e62e6dfa
commit
3be05556f9
@@ -1,3 +1,4 @@
|
||||
import type { LicenseState } from '@n8n/backend-common';
|
||||
import { Container } from '@n8n/di';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { DateTime } from 'luxon';
|
||||
@@ -38,12 +39,21 @@ describe('InsightsPruningService', () => {
|
||||
let insightsConfig: InsightsConfig;
|
||||
let insightsByPeriodRepository: InsightsByPeriodRepository;
|
||||
let insightsPruningService: InsightsPruningService;
|
||||
let licenseState: LicenseState;
|
||||
beforeAll(async () => {
|
||||
insightsConfig = Container.get(InsightsConfig);
|
||||
insightsConfig.maxAgeDays = 10;
|
||||
insightsConfig.pruneCheckIntervalHours = 1;
|
||||
insightsPruningService = Container.get(InsightsPruningService);
|
||||
insightsByPeriodRepository = Container.get(InsightsByPeriodRepository);
|
||||
licenseState = mock<LicenseState>({
|
||||
getInsightsRetentionMaxAge: () => insightsConfig.maxAgeDays,
|
||||
});
|
||||
insightsPruningService = new InsightsPruningService(
|
||||
insightsByPeriodRepository,
|
||||
insightsConfig,
|
||||
licenseState,
|
||||
mockLogger(),
|
||||
);
|
||||
});
|
||||
|
||||
test('old insights get pruned successfully', async () => {
|
||||
@@ -90,6 +100,58 @@ describe('InsightsPruningService', () => {
|
||||
expect(await insightsByPeriodRepository.count()).toBe(1);
|
||||
});
|
||||
|
||||
test.each<{ config: number; license: number; result: number }>([
|
||||
{
|
||||
config: -1,
|
||||
license: -1,
|
||||
result: Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
{
|
||||
config: -1,
|
||||
license: 5,
|
||||
result: 5,
|
||||
},
|
||||
{
|
||||
config: 5,
|
||||
license: -1,
|
||||
result: 5,
|
||||
},
|
||||
{
|
||||
config: 5,
|
||||
license: 10,
|
||||
result: 5,
|
||||
},
|
||||
{
|
||||
config: 10,
|
||||
license: 5,
|
||||
result: 5,
|
||||
},
|
||||
])(
|
||||
'pruningMaxAgeInDays is minimal age between license and config max age',
|
||||
async ({ config, license, result }) => {
|
||||
// ARRANGE
|
||||
const licenseState = mock<LicenseState>({
|
||||
getInsightsRetentionMaxAge() {
|
||||
return license;
|
||||
},
|
||||
});
|
||||
const insightsPruningService = new InsightsPruningService(
|
||||
insightsByPeriodRepository,
|
||||
mock<InsightsConfig>({
|
||||
maxAgeDays: config,
|
||||
}),
|
||||
licenseState,
|
||||
mockLogger(),
|
||||
);
|
||||
|
||||
// ACT
|
||||
const maxAge = insightsPruningService.pruningMaxAgeInDays;
|
||||
|
||||
// ASSERT
|
||||
expect(maxAge).toBe(result);
|
||||
},
|
||||
);
|
||||
|
||||
describe('pruning scheduling', () => {
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
@@ -111,6 +173,7 @@ describe('InsightsPruningService', () => {
|
||||
const insightsPruningService = new InsightsPruningService(
|
||||
insightsByPeriodRepository,
|
||||
insightsConfig,
|
||||
licenseState,
|
||||
mockLogger(),
|
||||
);
|
||||
const pruneSpy = jest.spyOn(insightsPruningService, 'pruneInsights');
|
||||
@@ -134,6 +197,7 @@ describe('InsightsPruningService', () => {
|
||||
const insightsPruningService = new InsightsPruningService(
|
||||
insightsByPeriodRepository,
|
||||
insightsConfig,
|
||||
licenseState,
|
||||
mockLogger(),
|
||||
);
|
||||
|
||||
|
||||
@@ -512,7 +512,6 @@ describe('getAvailableDateRanges', () => {
|
||||
mock<InsightsCollectionService>(),
|
||||
mock<InsightsPruningService>(),
|
||||
licenseMock,
|
||||
mock<InsightsConfig>(),
|
||||
mockLogger(),
|
||||
);
|
||||
});
|
||||
@@ -615,7 +614,6 @@ describe('getMaxAgeInDaysAndGranularity', () => {
|
||||
mock<InsightsCollectionService>(),
|
||||
mock<InsightsPruningService>(),
|
||||
licenseMock,
|
||||
mock<InsightsConfig>(),
|
||||
mockLogger(),
|
||||
);
|
||||
});
|
||||
@@ -704,7 +702,6 @@ describe('shutdown', () => {
|
||||
mockCollectionService,
|
||||
mockPruningService,
|
||||
mock<LicenseState>(),
|
||||
mock<InsightsConfig>(),
|
||||
mockLogger(),
|
||||
);
|
||||
});
|
||||
@@ -736,6 +733,7 @@ describe('timers', () => {
|
||||
const mockPruningService = mock<InsightsPruningService>({
|
||||
startPruningTimer: jest.fn(),
|
||||
stopPruningTimer: jest.fn(),
|
||||
isPruningEnabled: false,
|
||||
});
|
||||
|
||||
const mockedLogger = mockLogger();
|
||||
@@ -750,7 +748,6 @@ describe('timers', () => {
|
||||
mockCollectionService,
|
||||
mockPruningService,
|
||||
mock<LicenseState>(),
|
||||
mockedConfig,
|
||||
mockedLogger,
|
||||
);
|
||||
});
|
||||
@@ -768,6 +765,7 @@ describe('timers', () => {
|
||||
test('startTimers starts pruning timer', () => {
|
||||
// ARRANGE
|
||||
mockedConfig.maxAgeDays = 30;
|
||||
Object.defineProperty(mockPruningService, 'isPruningEnabled', { value: true });
|
||||
|
||||
// ACT
|
||||
insightsService.startTimers();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { LicenseState } from '@n8n/backend-common';
|
||||
import { Service } from '@n8n/di';
|
||||
import { strict } from 'assert';
|
||||
import { Logger } from 'n8n-core';
|
||||
@@ -18,11 +19,25 @@ export class InsightsPruningService {
|
||||
constructor(
|
||||
private readonly insightsByPeriodRepository: InsightsByPeriodRepository,
|
||||
private readonly config: InsightsConfig,
|
||||
private readonly licenseState: LicenseState,
|
||||
private readonly logger: Logger,
|
||||
) {
|
||||
this.logger = this.logger.scoped('insights');
|
||||
}
|
||||
|
||||
get isPruningEnabled() {
|
||||
return this.licenseState.getInsightsRetentionMaxAge() > -1 || this.config.maxAgeDays > -1;
|
||||
}
|
||||
|
||||
get pruningMaxAgeInDays() {
|
||||
const toMaxSafeIfUnlimited = (days: number) => (days === -1 ? Number.MAX_SAFE_INTEGER : days);
|
||||
|
||||
const licenseMaxAge = toMaxSafeIfUnlimited(this.licenseState.getInsightsRetentionMaxAge());
|
||||
const configMaxAge = toMaxSafeIfUnlimited(this.config.maxAgeDays);
|
||||
|
||||
return Math.min(licenseMaxAge, configMaxAge);
|
||||
}
|
||||
|
||||
startPruningTimer() {
|
||||
strict(this.isStopped);
|
||||
this.clearPruningTimer();
|
||||
@@ -57,7 +72,7 @@ export class InsightsPruningService {
|
||||
async pruneInsights() {
|
||||
this.logger.info('Pruning old insights data');
|
||||
try {
|
||||
const result = await this.insightsByPeriodRepository.pruneOldData(this.config.maxAgeDays);
|
||||
const result = await this.insightsByPeriodRepository.pruneOldData(this.pruningMaxAgeInDays);
|
||||
this.logger.debug(
|
||||
'Deleted insights by period',
|
||||
result.affected ? { count: result.affected } : {},
|
||||
|
||||
@@ -15,7 +15,6 @@ import { InsightsByPeriodRepository } from './database/repositories/insights-by-
|
||||
import { InsightsCollectionService } from './insights-collection.service';
|
||||
import { InsightsCompactionService } from './insights-compaction.service';
|
||||
import { InsightsPruningService } from './insights-pruning.service';
|
||||
import { InsightsConfig } from './insights.config';
|
||||
|
||||
const keyRangeToDays: Record<InsightsDateRange['key'], number> = {
|
||||
day: 1,
|
||||
@@ -35,20 +34,15 @@ export class InsightsService {
|
||||
private readonly collectionService: InsightsCollectionService,
|
||||
private readonly pruningService: InsightsPruningService,
|
||||
private readonly licenseState: LicenseState,
|
||||
private readonly config: InsightsConfig,
|
||||
private readonly logger: Logger,
|
||||
) {
|
||||
this.logger = this.logger.scoped('insights');
|
||||
}
|
||||
|
||||
get isPruningEnabled() {
|
||||
return this.config.maxAgeDays > -1;
|
||||
}
|
||||
|
||||
startTimers() {
|
||||
this.compactionService.startCompactionTimer();
|
||||
this.collectionService.startFlushingTimer();
|
||||
if (this.isPruningEnabled) {
|
||||
if (this.pruningService.isPruningEnabled) {
|
||||
this.pruningService.startPruningTimer();
|
||||
}
|
||||
this.logger.debug('Started compaction, flushing and pruning schedulers');
|
||||
|
||||
Reference in New Issue
Block a user