mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
Feat: remove last seen refactor flag (#5423)
What it says on the box --------- Signed-off-by: andreas-unleash <andreas@getunleash.ai> Co-authored-by: andreas-unleash <andreas@getunleash.ai>
This commit is contained in:
parent
ef8edf9c44
commit
e5760b5690
@ -102,7 +102,6 @@ exports[`should create default config 1`] = `
|
||||
"responseTimeWithAppNameKillSwitch": false,
|
||||
"scheduledConfigurationChanges": false,
|
||||
"strictSchemaValidation": false,
|
||||
"useLastSeenRefactor": false,
|
||||
},
|
||||
"externalResolver": {
|
||||
"getVariant": [Function],
|
||||
|
@ -189,9 +189,7 @@ export class FeatureToggleRowConverter {
|
||||
feature.createdAt = r.created_at;
|
||||
feature.favorite = r.favorite;
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
this.addLastSeenByEnvironment(feature, r);
|
||||
}
|
||||
this.addLastSeenByEnvironment(feature, r);
|
||||
|
||||
acc[r.name] = feature;
|
||||
return acc;
|
||||
@ -246,9 +244,7 @@ export class FeatureToggleRowConverter {
|
||||
feature.lastSeenAt = row.last_seen_at;
|
||||
feature.archivedAt = row.archived_at;
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
this.addLastSeenByEnvironment(feature, row);
|
||||
}
|
||||
this.addLastSeenByEnvironment(feature, row);
|
||||
|
||||
acc[row.name] = feature;
|
||||
return acc;
|
||||
|
@ -1970,13 +1970,7 @@ class FeatureToggleService {
|
||||
archived: boolean,
|
||||
userId: number,
|
||||
): Promise<FeatureToggle[]> {
|
||||
let features;
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
features = await this.featureToggleStore.getArchivedFeatures();
|
||||
} else {
|
||||
features = await this.featureToggleStore.getAll({ archived });
|
||||
}
|
||||
const features = await this.featureToggleStore.getArchivedFeatures();
|
||||
|
||||
if (this.flagResolver.isEnabled('privateProjects')) {
|
||||
const projectAccess =
|
||||
@ -1998,11 +1992,7 @@ class FeatureToggleService {
|
||||
archived: boolean,
|
||||
project: string,
|
||||
): Promise<FeatureToggle[]> {
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
return this.featureToggleStore.getArchivedFeatures(project);
|
||||
} else {
|
||||
return this.featureToggleStore.getAll({ archived, project });
|
||||
}
|
||||
return this.featureToggleStore.getArchivedFeatures(project);
|
||||
}
|
||||
|
||||
async getProjectId(name: string): Promise<string | undefined> {
|
||||
|
@ -171,15 +171,13 @@ export default class FeatureToggleStore implements IFeatureToggleStore {
|
||||
builder.addSelectColumn('ft.tag_value as tag_value');
|
||||
builder.addSelectColumn('ft.tag_type as tag_type');
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
builder.withLastSeenByEnvironment(archived);
|
||||
builder.addSelectColumn(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
builder.addSelectColumn(
|
||||
'last_seen_at_metrics.environment as last_seen_at_env',
|
||||
);
|
||||
}
|
||||
builder.withLastSeenByEnvironment(archived);
|
||||
builder.addSelectColumn(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
builder.addSelectColumn(
|
||||
'last_seen_at_metrics.environment as last_seen_at_env',
|
||||
);
|
||||
|
||||
if (userId) {
|
||||
builder.withFavorites(userId);
|
||||
|
@ -341,23 +341,21 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
|
||||
let selectColumns = ['features_view.*'] as (string | Raw<any>)[];
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'features_view.environment_name',
|
||||
).andOn(
|
||||
'last_seen_at_metrics.feature_name',
|
||||
'=',
|
||||
'features_view.name',
|
||||
);
|
||||
});
|
||||
// Override feature view for now
|
||||
selectColumns.push(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'features_view.environment_name',
|
||||
).andOn(
|
||||
'last_seen_at_metrics.feature_name',
|
||||
'=',
|
||||
'features_view.name',
|
||||
);
|
||||
}
|
||||
});
|
||||
// Override feature view for now
|
||||
selectColumns.push(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
|
||||
if (userId) {
|
||||
query = query.leftJoin(`favorite_features`, function () {
|
||||
@ -631,19 +629,17 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
'segments.id',
|
||||
);
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'environments.name',
|
||||
).andOn(
|
||||
'last_seen_at_metrics.feature_name',
|
||||
'=',
|
||||
'features.name',
|
||||
);
|
||||
});
|
||||
}
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'environments.name',
|
||||
).andOn(
|
||||
'last_seen_at_metrics.feature_name',
|
||||
'=',
|
||||
'features.name',
|
||||
);
|
||||
});
|
||||
|
||||
let selectColumns = [
|
||||
'features.name as feature_name',
|
||||
@ -664,11 +660,8 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
'segments.name as segment_name',
|
||||
] as (string | Raw<any> | Knex.QueryBuilder)[];
|
||||
|
||||
let lastSeenQuery = 'feature_environments.last_seen_at';
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
lastSeenQuery = 'last_seen_at_metrics.last_seen_at';
|
||||
selectColumns.push(`${lastSeenQuery} as env_last_seen_at`);
|
||||
}
|
||||
const lastSeenQuery = 'last_seen_at_metrics.last_seen_at';
|
||||
selectColumns.push(`${lastSeenQuery} as env_last_seen_at`);
|
||||
|
||||
if (userId) {
|
||||
query.leftJoin(`favorite_features`, function () {
|
||||
@ -802,19 +795,13 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
)
|
||||
.leftJoin('feature_tag as ft', 'ft.feature_name', 'features.name');
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'environments.name',
|
||||
).andOn(
|
||||
'last_seen_at_metrics.feature_name',
|
||||
'=',
|
||||
'features.name',
|
||||
);
|
||||
});
|
||||
}
|
||||
query.leftJoin('last_seen_at_metrics', function () {
|
||||
this.on(
|
||||
'last_seen_at_metrics.environment',
|
||||
'=',
|
||||
'environments.name',
|
||||
).andOn('last_seen_at_metrics.feature_name', '=', 'features.name');
|
||||
});
|
||||
|
||||
let selectColumns = [
|
||||
'features.name as feature_name',
|
||||
@ -833,15 +820,9 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
|
||||
'ft.tag_type as tag_type',
|
||||
] as (string | Raw<any> | Knex.QueryBuilder)[];
|
||||
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
selectColumns.push(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
} else {
|
||||
selectColumns.push(
|
||||
'feature_environments.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
}
|
||||
selectColumns.push(
|
||||
'last_seen_at_metrics.last_seen_at as env_last_seen_at',
|
||||
);
|
||||
|
||||
if (userId) {
|
||||
query = query.leftJoin(`favorite_features`, function () {
|
||||
|
@ -22,7 +22,6 @@ beforeAll(async () => {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
useLastSeenRefactor: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -689,9 +689,7 @@ test('Should return last seen at per environment', async () => {
|
||||
expect(environments[0].lastSeenAt).toEqual(new Date(date));
|
||||
|
||||
// Test with feature flag on
|
||||
const config = createTestConfig({
|
||||
experimental: { flags: { useLastSeenRefactor: true } },
|
||||
});
|
||||
const config = createTestConfig();
|
||||
|
||||
const featureService = createFeatureToggleService(db.rawDatabase, config);
|
||||
|
||||
|
@ -20,7 +20,6 @@ beforeAll(async () => {
|
||||
strictSchemaValidation: true,
|
||||
strategyVariant: true,
|
||||
privateProjects: true,
|
||||
useLastSeenRefactor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -185,34 +185,6 @@ test('schema allow yes=<string nbr>', () => {
|
||||
expect(value.bucket.toggles.Demo2.no).toBe(256);
|
||||
});
|
||||
|
||||
test('should set lastSeen on toggle', async () => {
|
||||
expect.assertions(1);
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'toggleLastSeen',
|
||||
});
|
||||
await request
|
||||
.post('/api/client/metrics')
|
||||
.send({
|
||||
appName: 'demo',
|
||||
bucket: {
|
||||
start: Date.now(),
|
||||
stop: Date.now(),
|
||||
toggles: {
|
||||
toggleLastSeen: {
|
||||
yes: 200,
|
||||
no: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
.expect(202);
|
||||
|
||||
await services.lastSeenService.store();
|
||||
const toggle = await stores.featureToggleStore.get('toggleLastSeen');
|
||||
|
||||
expect(toggle.lastSeenAt).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should return a 400 when required fields are missing', async () => {
|
||||
stores.featureToggleStore.create('default', {
|
||||
name: 'toggleLastSeen',
|
||||
|
@ -1,39 +0,0 @@
|
||||
import { Logger } from '../../../logger';
|
||||
import { IFeatureOverview } from '../../../types';
|
||||
import { IFeatureLastSeenResults } from './last-seen-read-model';
|
||||
|
||||
export class LastSeenMapper {
|
||||
mapToFeatures(
|
||||
features: IFeatureOverview[],
|
||||
lastSeenAtPerEnvironment: IFeatureLastSeenResults,
|
||||
logger: Logger,
|
||||
): IFeatureOverview[] {
|
||||
return features.map((feature) => {
|
||||
if (!feature.environments) {
|
||||
logger.warn('Feature without environments:', feature);
|
||||
return feature;
|
||||
}
|
||||
|
||||
feature.environments = feature.environments.map((environment) => {
|
||||
const noData =
|
||||
!lastSeenAtPerEnvironment[feature.name] ||
|
||||
!lastSeenAtPerEnvironment[feature.name][environment.name];
|
||||
|
||||
if (noData) {
|
||||
logger.warn(
|
||||
'No last seen data for environment:',
|
||||
environment,
|
||||
);
|
||||
return environment;
|
||||
}
|
||||
|
||||
environment.lastSeenAt = new Date(
|
||||
lastSeenAtPerEnvironment[feature.name][environment.name]
|
||||
.lastSeen,
|
||||
);
|
||||
return environment;
|
||||
});
|
||||
return feature;
|
||||
});
|
||||
}
|
||||
}
|
@ -51,11 +51,7 @@ export class LastSeenService {
|
||||
`Updating last seen for ${lastSeenToggles.length} toggles`,
|
||||
);
|
||||
|
||||
if (this.config.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
await this.lastSeenStore.setLastSeen(lastSeenToggles);
|
||||
} else {
|
||||
await this.featureToggleStore.setLastSeen(lastSeenToggles);
|
||||
}
|
||||
await this.lastSeenStore.setLastSeen(lastSeenToggles);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@ -81,8 +77,6 @@ export class LastSeenService {
|
||||
}
|
||||
|
||||
async cleanLastSeen() {
|
||||
if (this.flagResolver.isEnabled('useLastSeenRefactor')) {
|
||||
await this.lastSeenStore.cleanLastSeen();
|
||||
}
|
||||
await this.lastSeenStore.cleanLastSeen();
|
||||
}
|
||||
}
|
||||
|
@ -1,95 +0,0 @@
|
||||
import { IFeatureOverview } from '../../../../types';
|
||||
import { LastSeenMapper } from '../last-seen-mapper';
|
||||
import getLogger from '../../../../../test/fixtures/no-logger';
|
||||
|
||||
test('should produce correct output when mapped', () => {
|
||||
const mapper = new LastSeenMapper();
|
||||
|
||||
const inputLastSeen = {
|
||||
exp: {
|
||||
production: { lastSeen: '2023-10-05T07:27:04.286Z' },
|
||||
development: { lastSeen: '2023-10-04T19:03:29.682Z' },
|
||||
},
|
||||
'payment-system': {
|
||||
production: { lastSeen: '2023-10-05T07:27:04.286Z' },
|
||||
development: { lastSeen: '2023-10-04T19:03:29.682Z' },
|
||||
},
|
||||
};
|
||||
|
||||
const inputFeatures: IFeatureOverview[] = [
|
||||
{
|
||||
type: 'release',
|
||||
description: null,
|
||||
favorite: false,
|
||||
name: 'payment-system',
|
||||
// @ts-ignore
|
||||
createdAt: '2023-06-30T12:57:20.476Z',
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-03T13:08:16.263Z',
|
||||
stale: false,
|
||||
impressionData: false,
|
||||
environments: [
|
||||
{
|
||||
name: 'development',
|
||||
enabled: false,
|
||||
type: 'development',
|
||||
sortOrder: 2,
|
||||
variantCount: 0,
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-04T19:03:29.682Z',
|
||||
},
|
||||
{
|
||||
name: 'production',
|
||||
enabled: true,
|
||||
type: 'production',
|
||||
sortOrder: 3,
|
||||
variantCount: 0,
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-05T07:27:04.286Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'experiment',
|
||||
description: null,
|
||||
favorite: false,
|
||||
name: 'exp',
|
||||
// @ts-ignore
|
||||
createdAt: '2023-09-13T08:08:28.211Z',
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-03T13:08:16.263Z',
|
||||
stale: false,
|
||||
impressionData: false,
|
||||
environments: [
|
||||
{
|
||||
name: 'development',
|
||||
enabled: false,
|
||||
type: 'development',
|
||||
sortOrder: 2,
|
||||
variantCount: 0,
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-04T19:03:29.682Z',
|
||||
},
|
||||
{
|
||||
name: 'production',
|
||||
enabled: true,
|
||||
type: 'production',
|
||||
sortOrder: 3,
|
||||
variantCount: 0,
|
||||
// @ts-ignore
|
||||
lastSeenAt: '2023-10-05T07:27:04.286Z',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const logger = getLogger();
|
||||
|
||||
const result = mapper.mapToFeatures(inputFeatures, inputLastSeen, logger);
|
||||
|
||||
expect(result[0].environments[0].name).toBe('development');
|
||||
expect(result[0].name).toBe('payment-system');
|
||||
expect(result[0].environments[0].lastSeenAt).toEqual(
|
||||
new Date(inputLastSeen['payment-system'].development.lastSeen),
|
||||
);
|
||||
});
|
@ -16,7 +16,6 @@ beforeAll(async () => {
|
||||
experimental: {
|
||||
flags: {
|
||||
strictSchemaValidation: true,
|
||||
useLastSeenRefactor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -36,7 +36,9 @@ function initLastSeenService(flagEnabled = true) {
|
||||
}
|
||||
|
||||
test('should not add duplicates per feature/environment', async () => {
|
||||
const { lastSeenService, featureToggleStore } = initLastSeenService(false);
|
||||
const { lastSeenService, featureToggleStore, lastSeenStore } =
|
||||
initLastSeenService(false);
|
||||
const lastSeenSpy = jest.spyOn(lastSeenStore, 'setLastSeen');
|
||||
|
||||
lastSeenService.updateLastSeen([
|
||||
{
|
||||
@ -59,10 +61,8 @@ test('should not add duplicates per feature/environment', async () => {
|
||||
timestamp: new Date(),
|
||||
},
|
||||
]);
|
||||
featureToggleStore.setLastSeen = jest.fn();
|
||||
await lastSeenService.store();
|
||||
|
||||
expect(featureToggleStore.setLastSeen).toHaveBeenCalledWith([
|
||||
expect(lastSeenSpy).toHaveBeenCalledWith([
|
||||
{
|
||||
environment: 'development',
|
||||
featureName: 'myFeature',
|
||||
@ -96,7 +96,6 @@ test('should call last seen at store with correct data', async () => {
|
||||
},
|
||||
]);
|
||||
lastSeenStore.setLastSeen = jest.fn();
|
||||
featureToggleStore.setLastSeen = jest.fn();
|
||||
await lastSeenService.store();
|
||||
|
||||
expect(lastSeenStore.setLastSeen).toHaveBeenCalledWith([
|
||||
@ -105,5 +104,4 @@ test('should call last seen at store with correct data', async () => {
|
||||
featureName: 'myFeature',
|
||||
},
|
||||
]);
|
||||
expect(featureToggleStore.setLastSeen).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
122
src/lib/services/scheduler-service.test.ts
Normal file
122
src/lib/services/scheduler-service.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { LogProvider } from '../logger';
|
||||
import { SchedulerService } from '../features/scheduler/scheduler-service';
|
||||
import { createTestConfig } from '../../test/config/test-config';
|
||||
import FakeSettingStore from '../../test/fixtures/fake-setting-store';
|
||||
import SettingService from './setting-service';
|
||||
import EventService from './event-service';
|
||||
import MaintenanceService from '../features/maintenance/maintenance-service';
|
||||
|
||||
function ms(timeMs) {
|
||||
return new Promise((resolve) => setTimeout(resolve, timeMs));
|
||||
}
|
||||
|
||||
const getLogger = () => {
|
||||
const records: any[] = [];
|
||||
const logger: LogProvider = () => ({
|
||||
error(...args: any[]) {
|
||||
records.push(args);
|
||||
},
|
||||
debug() {},
|
||||
info() {},
|
||||
warn() {},
|
||||
fatal() {},
|
||||
});
|
||||
const getRecords = () => {
|
||||
return records;
|
||||
};
|
||||
|
||||
return { logger, getRecords };
|
||||
};
|
||||
|
||||
let schedulerService: SchedulerService;
|
||||
let getRecords;
|
||||
|
||||
beforeEach(() => {
|
||||
const config = createTestConfig();
|
||||
const settingStore = new FakeSettingStore();
|
||||
const settingService = new SettingService({ settingStore }, config, {
|
||||
storeEvent() {},
|
||||
} as unknown as EventService);
|
||||
const maintenanceService = new MaintenanceService(config, settingService);
|
||||
const { logger, getRecords: getRecordsFn } = getLogger();
|
||||
getRecords = getRecordsFn;
|
||||
|
||||
schedulerService = new SchedulerService(
|
||||
logger,
|
||||
maintenanceService,
|
||||
config.eventBus,
|
||||
);
|
||||
});
|
||||
|
||||
test('Schedules job immediately', async () => {
|
||||
const job = jest.fn();
|
||||
await schedulerService.schedule(job, 10, 'test-id');
|
||||
|
||||
expect(job).toBeCalledTimes(1);
|
||||
schedulerService.stop();
|
||||
});
|
||||
|
||||
test('Can schedule a single regular job', async () => {
|
||||
const job = jest.fn();
|
||||
await schedulerService.schedule(job, 50, 'test-id-3');
|
||||
await ms(75);
|
||||
|
||||
expect(job).toBeCalledTimes(2);
|
||||
schedulerService.stop();
|
||||
});
|
||||
|
||||
test('Can schedule multiple jobs at the same interval', async () => {
|
||||
const job = jest.fn();
|
||||
const anotherJob = jest.fn();
|
||||
|
||||
await schedulerService.schedule(job, 50, 'test-id-6');
|
||||
await schedulerService.schedule(anotherJob, 50, 'test-id-7');
|
||||
await ms(75);
|
||||
|
||||
expect(job).toBeCalledTimes(2);
|
||||
expect(anotherJob).toBeCalledTimes(2);
|
||||
schedulerService.stop();
|
||||
});
|
||||
|
||||
test('Can schedule multiple jobs at the different intervals', async () => {
|
||||
const job = jest.fn();
|
||||
const anotherJob = jest.fn();
|
||||
|
||||
await schedulerService.schedule(job, 100, 'test-id-8');
|
||||
await schedulerService.schedule(anotherJob, 200, 'test-id-9');
|
||||
await ms(250);
|
||||
|
||||
expect(job).toBeCalledTimes(3);
|
||||
expect(anotherJob).toBeCalledTimes(2);
|
||||
schedulerService.stop();
|
||||
});
|
||||
|
||||
test('Can handle crash of a async job', async () => {
|
||||
const job = async () => {
|
||||
await Promise.reject('async reason');
|
||||
};
|
||||
|
||||
await schedulerService.schedule(job, 50, 'test-id-10');
|
||||
await ms(75);
|
||||
|
||||
schedulerService.stop();
|
||||
expect(getRecords()).toEqual([
|
||||
['scheduled job failed | id: test-id-10 | async reason'],
|
||||
['scheduled job failed | id: test-id-10 | async reason'],
|
||||
]);
|
||||
});
|
||||
|
||||
test('Can handle crash of a sync job', async () => {
|
||||
const job = () => {
|
||||
throw new Error('sync reason');
|
||||
};
|
||||
|
||||
await schedulerService.schedule(job, 50, 'test-id-11');
|
||||
await ms(75);
|
||||
|
||||
schedulerService.stop();
|
||||
expect(getRecords()).toEqual([
|
||||
['scheduled job failed | id: test-id-11 | Error: sync reason'],
|
||||
['scheduled job failed | id: test-id-11 | Error: sync reason'],
|
||||
]);
|
||||
});
|
@ -25,7 +25,6 @@ export type IFlagKey =
|
||||
| 'customRootRolesKillSwitch'
|
||||
| 'privateProjects'
|
||||
| 'disableMetrics'
|
||||
| 'useLastSeenRefactor'
|
||||
| 'banners'
|
||||
| 'featureSearchAPI'
|
||||
| 'featureSearchFrontend'
|
||||
@ -114,10 +113,6 @@ const flags: IFlags = {
|
||||
process.env.UNLEASH_EXPERIMENTAL_DISABLE_METRICS,
|
||||
false,
|
||||
),
|
||||
useLastSeenRefactor: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_USE_LAST_SEEN_REFACTOR,
|
||||
false,
|
||||
),
|
||||
featureSearchAPI: parseEnvVarBoolean(
|
||||
process.env.UNLEASH_EXPERIMENTAL_FEATURE_SEARCH_API,
|
||||
false,
|
||||
|
@ -38,7 +38,6 @@ process.nextTick(async () => {
|
||||
anonymiseEventLog: false,
|
||||
responseTimeWithAppNameKillSwitch: false,
|
||||
privateProjects: true,
|
||||
useLastSeenRefactor: true,
|
||||
featureSearchAPI: true,
|
||||
featureSearchFrontend: false,
|
||||
},
|
||||
|
@ -77,14 +77,13 @@ test('returns three archived toggles', async () => {
|
||||
});
|
||||
|
||||
test('returns three archived toggles with archivedAt', async () => {
|
||||
expect.assertions(3);
|
||||
expect.assertions(2);
|
||||
return app.request
|
||||
.get('/api/admin/archive/features')
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.features.length).toEqual(3);
|
||||
expect(res.body.features.every((f) => f.archived)).toEqual(true);
|
||||
expect(res.body.features.every((f) => f.archivedAt)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
@ -14,6 +14,7 @@ let app: IUnleashTest;
|
||||
let db: ITestDb;
|
||||
|
||||
let projectStore: IProjectStore;
|
||||
const testDate = '2023-10-01T12:34:56.000Z';
|
||||
|
||||
beforeAll(async () => {
|
||||
db = await dbInit('projects_api_serial', getLogger);
|
||||
@ -146,7 +147,12 @@ test('response for default project should include created_at', async () => {
|
||||
test('response should include last seen at per environment', async () => {
|
||||
await app.createFeature('my-new-feature-toggle');
|
||||
|
||||
await insertLastSeenAt('my-new-feature-toggle', db.rawDatabase, 'default');
|
||||
await insertLastSeenAt(
|
||||
'my-new-feature-toggle',
|
||||
db.rawDatabase,
|
||||
'default',
|
||||
testDate,
|
||||
);
|
||||
await insertFeatureEnvironmentsLastSeen(
|
||||
'my-new-feature-toggle',
|
||||
db.rawDatabase,
|
||||
@ -158,19 +164,11 @@ test('response should include last seen at per environment', async () => {
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body.features[0].environments[0].lastSeenAt).toEqual(
|
||||
'2022-05-01T12:34:56.000Z',
|
||||
);
|
||||
expect(body.features[0].environments[0].lastSeenAt).toEqual(testDate);
|
||||
|
||||
const appWithLastSeenRefactor = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {
|
||||
useLastSeenRefactor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{},
|
||||
db.rawDatabase,
|
||||
);
|
||||
|
||||
@ -187,13 +185,7 @@ test('response should include last seen at per environment', async () => {
|
||||
test('response should include last seen at per environment for multiple environments', async () => {
|
||||
const appWithLastSeenRefactor = await setupAppWithCustomConfig(
|
||||
db.stores,
|
||||
{
|
||||
experimental: {
|
||||
flags: {
|
||||
useLastSeenRefactor: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{},
|
||||
db.rawDatabase,
|
||||
);
|
||||
await app.createFeature('my-new-feature-toggle');
|
||||
|
@ -1,143 +0,0 @@
|
||||
import { createTestConfig } from '../../config/test-config';
|
||||
import dbInit from '../helpers/database-init';
|
||||
import { IUnleashStores } from '../../../lib/types/stores';
|
||||
import { LastSeenService } from '../../../lib/services/client-metrics/last-seen/last-seen-service';
|
||||
import { IClientMetricsEnv } from '../../../lib/types/stores/client-metrics-store-v2';
|
||||
|
||||
let stores: IUnleashStores;
|
||||
let db;
|
||||
let config;
|
||||
|
||||
beforeAll(async () => {
|
||||
config = createTestConfig();
|
||||
db = await dbInit('last_seen_service_serial', config.getLogger);
|
||||
stores = db.stores;
|
||||
});
|
||||
beforeEach(async () => {
|
||||
await stores.featureToggleStore.deleteAll();
|
||||
});
|
||||
afterAll(async () => {
|
||||
await db.destroy();
|
||||
});
|
||||
|
||||
test('Should update last seen for known toggles', async () => {
|
||||
const service = new LastSeenService(
|
||||
{
|
||||
lastSeenStore: stores.lastSeenStore,
|
||||
featureToggleStore: stores.featureToggleStore,
|
||||
},
|
||||
config,
|
||||
);
|
||||
const time = Date.now() - 100;
|
||||
await stores.featureToggleStore.create('default', { name: 'ta1' });
|
||||
|
||||
const metrics: IClientMetricsEnv[] = [
|
||||
{
|
||||
featureName: 'ta1',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 1,
|
||||
no: 0,
|
||||
},
|
||||
{
|
||||
featureName: 'ta2',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 1,
|
||||
no: 0,
|
||||
},
|
||||
];
|
||||
|
||||
service.updateLastSeen(metrics);
|
||||
await service.store();
|
||||
|
||||
const t1 = await stores.featureToggleStore.get('ta1');
|
||||
|
||||
expect(t1.lastSeenAt.getTime()).toBeGreaterThan(time);
|
||||
});
|
||||
|
||||
test('Should not update last seen toggles with 0 metrics', async () => {
|
||||
// jest.useFakeTimers();
|
||||
const service = new LastSeenService(
|
||||
{
|
||||
lastSeenStore: stores.lastSeenStore,
|
||||
featureToggleStore: stores.featureToggleStore,
|
||||
},
|
||||
config,
|
||||
);
|
||||
const time = Date.now();
|
||||
await stores.featureToggleStore.create('default', { name: 'tb1' });
|
||||
await stores.featureToggleStore.create('default', { name: 'tb2' });
|
||||
|
||||
const metrics: IClientMetricsEnv[] = [
|
||||
{
|
||||
featureName: 'tb1',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 1,
|
||||
no: 0,
|
||||
},
|
||||
{
|
||||
featureName: 'tb2',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
];
|
||||
|
||||
service.updateLastSeen(metrics);
|
||||
|
||||
// bypass interval waiting
|
||||
await service.store();
|
||||
|
||||
const t1 = await stores.featureToggleStore.get('tb1');
|
||||
const t2 = await stores.featureToggleStore.get('tb2');
|
||||
|
||||
expect(t2.lastSeenAt).toBeNull();
|
||||
expect(t1.lastSeenAt.getTime()).toBeGreaterThanOrEqual(time);
|
||||
});
|
||||
|
||||
test('Should not update anything for 0 toggles', async () => {
|
||||
// jest.useFakeTimers();
|
||||
const service = new LastSeenService(
|
||||
{
|
||||
lastSeenStore: stores.lastSeenStore,
|
||||
featureToggleStore: stores.featureToggleStore,
|
||||
},
|
||||
config,
|
||||
);
|
||||
const time = Date.now();
|
||||
await stores.featureToggleStore.create('default', { name: 'tb1' });
|
||||
await stores.featureToggleStore.create('default', { name: 'tb2' });
|
||||
|
||||
const metrics: IClientMetricsEnv[] = [
|
||||
{
|
||||
featureName: 'tb1',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
{
|
||||
featureName: 'tb2',
|
||||
appName: 'some-App',
|
||||
environment: 'default',
|
||||
timestamp: new Date(time),
|
||||
yes: 0,
|
||||
no: 0,
|
||||
},
|
||||
];
|
||||
|
||||
service.updateLastSeen(metrics);
|
||||
|
||||
// bypass interval waiting
|
||||
const count = await service.store();
|
||||
|
||||
expect(count).toBe(0);
|
||||
});
|
Loading…
Reference in New Issue
Block a user