1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

chore: remove new frontend api feature flag (#6906)

The flag has been 100% for a bit now, we need to prepare for GA.
This commit is contained in:
Jaanus Sellin 2024-04-24 09:15:57 +03:00 committed by GitHub
parent bf3366434c
commit d578deab7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 29 additions and 466 deletions

View File

@ -121,7 +121,6 @@ exports[`should create default config 1`] = `
}, },
}, },
"filterInvalidClientMetrics": false, "filterInvalidClientMetrics": false,
"globalFrontendApiCache": false,
"googleAuthEnabled": false, "googleAuthEnabled": false,
"inMemoryScheduledChangeRequests": false, "inMemoryScheduledChangeRequests": false,
"maintenanceMode": false, "maintenanceMode": false,
@ -144,7 +143,6 @@ exports[`should create default config 1`] = `
"queryMissingTokens": false, "queryMissingTokens": false,
"responseTimeMetricsFix": false, "responseTimeMetricsFix": false,
"responseTimeWithAppNameKillSwitch": false, "responseTimeWithAppNameKillSwitch": false,
"returnGlobalFrontendApiCache": false,
"scimApi": false, "scimApi": false,
"showInactiveUsers": false, "showInactiveUsers": false,
"signals": false, "signals": false,

View File

@ -10,7 +10,6 @@ import {
emptyResponse, emptyResponse,
getStandardResponses, getStandardResponses,
type FrontendApiClientSchema, type FrontendApiClientSchema,
type FrontendApiFeatureSchema,
frontendApiFeaturesSchema, frontendApiFeaturesSchema,
type FrontendApiFeaturesSchema, type FrontendApiFeaturesSchema,
} from '../../openapi'; } from '../../openapi';
@ -21,8 +20,6 @@ import NotImplementedError from '../../error/not-implemented-error';
import NotFoundError from '../../error/notfound-error'; import NotFoundError from '../../error/notfound-error';
import rateLimit from 'express-rate-limit'; import rateLimit from 'express-rate-limit';
import { minutesToMilliseconds } from 'date-fns'; import { minutesToMilliseconds } from 'date-fns';
import isEqual from 'lodash.isequal';
import { diff } from 'json-diff';
import metricsHelper from '../../util/metrics-helper'; import metricsHelper from '../../util/metrics-helper';
import { FUNCTION_TIME } from '../../metric-events'; import { FUNCTION_TIME } from '../../metric-events';
@ -186,48 +183,11 @@ export default class FrontendAPIController extends Controller {
if (!this.config.flagResolver.isEnabled('embedProxy')) { if (!this.config.flagResolver.isEnabled('embedProxy')) {
throw new NotFoundError(); throw new NotFoundError();
} }
let toggles: FrontendApiFeatureSchema[]; const toggles =
let newToggles: FrontendApiFeatureSchema[] = []; await this.services.frontendApiService.getFrontendApiFeatures(
if (this.config.flagResolver.isEnabled('globalFrontendApiCache')) { req.user,
const context = FrontendAPIController.createContext(req); FrontendAPIController.createContext(req),
[toggles, newToggles] = await Promise.all([
this.getTimedFrontendApiFeatures(req, context),
this.getTimedNewFrontendApiFeatures(req, context),
]);
const sortedToggles = toggles.sort((a, b) =>
a.name.localeCompare(b.name),
); );
const sortedNewToggles = newToggles.sort((a, b) =>
a.name.localeCompare(b.name),
);
if (!isEqual(sortedToggles, sortedNewToggles)) {
this.logger.warn(
`old features and new features are different. Old count ${
toggles.length
}, new count ${newToggles.length}, projects ${
req.user.projects
}, environment ${
req.user.environment
}, diff ${JSON.stringify(
diff(sortedToggles, sortedNewToggles),
)}`,
);
}
} else if (
this.config.flagResolver.isEnabled('returnGlobalFrontendApiCache')
) {
toggles =
await this.services.frontendApiService.getNewFrontendApiFeatures(
req.user,
FrontendAPIController.createContext(req),
);
} else {
toggles =
await this.services.frontendApiService.getFrontendApiFeatures(
req.user,
FrontendAPIController.createContext(req),
);
}
res.set('Cache-control', 'no-cache'); res.set('Cache-control', 'no-cache');
@ -239,28 +199,6 @@ export default class FrontendAPIController extends Controller {
); );
} }
private async getTimedFrontendApiFeatures(req, context) {
const stopTimer = this.timer('getFrontendApiFeatures');
const features =
await this.services.frontendApiService.getFrontendApiFeatures(
req.user,
context,
);
stopTimer();
return features;
}
private async getTimedNewFrontendApiFeatures(req, context) {
const stopTimer = this.timer('getNewFrontendApiFeatures');
const features =
await this.services.frontendApiService.getNewFrontendApiFeatures(
req.user,
context,
);
stopTimer();
return features;
}
private async registerFrontendApiMetrics( private async registerFrontendApiMetrics(
req: ApiUserRequest<unknown, unknown, ClientMetricsSchema>, req: ApiUserRequest<unknown, unknown, ClientMetricsSchema>,
res: Response, res: Response,

View File

@ -49,7 +49,6 @@ export class FrontendApiRepository
} }
getToggle(name: string): FeatureInterface { getToggle(name: string): FeatureInterface {
//@ts-ignore (we must update the node SDK to allow undefined)
return this.globalFrontendApiCache.getToggle(name, this.token); return this.globalFrontendApiCache.getToggle(name, this.token);
} }

View File

@ -1,155 +0,0 @@
import {
type IApiUser,
type IUnleashConfig,
type IUnleashStores,
TEST_AUDIT_USER,
} from '../../types';
import dbInit, { type ITestDb } from '../../../test/e2e/helpers/database-init';
import type { FrontendApiService } from './frontend-api-service';
import { createFrontendApiService } from './createFrontendApiService';
import { createLastSeenService } from '../metrics/last-seen/createLastSeenService';
import ClientMetricsServiceV2 from '../metrics/client-metrics/metrics-service-v2';
import ConfigurationRevisionService from '../feature-toggle/configuration-revision-service';
import getLogger from '../../../test/fixtures/no-logger';
import { createTestConfig } from '../../../test/config/test-config';
import { ApiTokenType } from '../../types/models/api-token';
import type FeatureToggleService from '../feature-toggle/feature-toggle-service';
import { createFeatureToggleService } from '../feature-toggle/createFeatureToggleService';
import {
FRONTEND_API_REPOSITORY_CREATED,
PROXY_REPOSITORY_CREATED,
} from '../../metric-events';
let stores: IUnleashStores;
let db: ITestDb;
let frontendApiService: FrontendApiService;
let featureToggleService: FeatureToggleService;
let configurationRevisionService: ConfigurationRevisionService;
let config: IUnleashConfig;
beforeAll(async () => {
db = await dbInit('frontend_api_service', getLogger);
stores = db.stores;
config = createTestConfig({
experimental: {
flags: {
globalFrontendApiCache: true,
},
},
});
const lastSeenService = createLastSeenService(db.rawDatabase, config);
const clientMetricsServiceV2 = new ClientMetricsServiceV2(
stores,
config,
lastSeenService,
);
configurationRevisionService = ConfigurationRevisionService.getInstance(
stores,
config,
);
frontendApiService = createFrontendApiService(
db.rawDatabase,
config,
clientMetricsServiceV2,
configurationRevisionService,
);
featureToggleService = createFeatureToggleService(db.rawDatabase, config);
});
afterAll(async () => {
await db.destroy();
});
const createProject = async (project: string) => {
await stores.projectStore.create({
name: project,
description: '',
id: project,
});
await stores.projectStore.addEnvironmentToProject(project, 'development');
await stores.projectStore.addEnvironmentToProject(project, 'production');
};
const createEnvironment = async (environment: string) => {
await stores.environmentStore.create({ name: environment, type: 'test' });
};
const createFeature = async (project: string, featureName: string) => {
await featureToggleService.createFeatureToggle(
project,
{ name: featureName, description: '' },
TEST_AUDIT_USER,
);
};
const enableFeature = async (
project: string,
featureName: string,
environment: string,
) => {
await featureToggleService.unprotectedUpdateEnabled(
project,
featureName,
environment,
true,
TEST_AUDIT_USER,
);
};
test('Compare Frontend API implementations', async () => {
const projectA = 'projectA';
const projectB = 'projectB';
await createEnvironment('development');
await createEnvironment('production');
await createProject(projectA);
await createProject(projectB);
await createFeature(projectA, 'featureA'); // include
await createFeature(projectA, 'featureB'); // another env
await createFeature(projectA, 'featureC'); // not enabled
await createFeature(projectA, 'featureD'); // include
await enableFeature(projectA, 'featureA', 'development');
await enableFeature(projectA, 'featureD', 'development');
await enableFeature(projectA, 'featureB', 'production');
await createFeature(projectB, 'featureE'); // another project
await enableFeature(projectB, 'featureE', 'development');
await configurationRevisionService.updateMaxRevisionId();
let proxyRepositoriesCount = 0;
config.eventBus.on(PROXY_REPOSITORY_CREATED, () => {
proxyRepositoriesCount++;
});
let frontendRepositoriesCount = 0;
config.eventBus.on(FRONTEND_API_REPOSITORY_CREATED, () => {
frontendRepositoriesCount++;
});
const oldFeatures = await frontendApiService.getFrontendApiFeatures(
{
projects: [projectA],
environment: 'development',
type: ApiTokenType.FRONTEND,
} as IApiUser,
{ sessionId: '1234' },
);
const newFeatures = await frontendApiService.getNewFrontendApiFeatures(
{
projects: [projectA],
environment: 'development',
type: ApiTokenType.FRONTEND,
} as IApiUser,
{ sessionId: '1234' },
);
expect(proxyRepositoriesCount).toBe(1);
expect(frontendRepositoriesCount).toBe(1);
expect(oldFeatures).toEqual(newFeatures);
expect(newFeatures.length).toBe(2);
});

View File

@ -52,7 +52,7 @@ test('frontend api service fetching features from global cache', async () => {
globalFrontendApiCache, globalFrontendApiCache,
); );
const features = await frontendApiService.getNewFrontendApiFeatures( const features = await frontendApiService.getFrontendApiFeatures(
{ {
projects: ['irrelevant'], projects: ['irrelevant'],
environment: 'irrelevant', environment: 'irrelevant',

View File

@ -25,13 +25,9 @@ import {
} from '../../types/settings/frontend-settings'; } from '../../types/settings/frontend-settings';
import { validateOrigins } from '../../util'; import { validateOrigins } from '../../util';
import { BadDataError, InvalidTokenError } from '../../error'; import { BadDataError, InvalidTokenError } from '../../error';
import { import { FRONTEND_API_REPOSITORY_CREATED } from '../../metric-events';
FRONTEND_API_REPOSITORY_CREATED,
PROXY_REPOSITORY_CREATED,
} from '../../metric-events';
import { FrontendApiRepository } from './frontend-api-repository'; import { FrontendApiRepository } from './frontend-api-repository';
import type { GlobalFrontendApiCache } from './global-frontend-api-cache'; import type { GlobalFrontendApiCache } from './global-frontend-api-cache';
import { ProxyRepository } from './proxy-repository';
export type Config = Pick< export type Config = Pick<
IUnleashConfig, IUnleashConfig,
@ -66,8 +62,6 @@ export class FrontendApiService {
*/ */
private readonly clients: Map<ApiUser['secret'], Promise<Unleash>> = private readonly clients: Map<ApiUser['secret'], Promise<Unleash>> =
new Map(); new Map();
private readonly newClients: Map<ApiUser['secret'], Promise<Unleash>> =
new Map();
private cachedFrontendSettings?: FrontendSettings; private cachedFrontendSettings?: FrontendSettings;
@ -93,34 +87,6 @@ export class FrontendApiService {
const sessionId = const sessionId =
context.sessionId || crypto.randomBytes(18).toString('hex'); context.sessionId || crypto.randomBytes(18).toString('hex');
const resultDefinitions = definitions
.filter((feature) =>
client.isEnabled(feature.name, {
...context,
sessionId,
}),
)
.map((feature) => ({
name: feature.name,
enabled: Boolean(feature.enabled),
variant: client.getVariant(feature.name, {
...context,
sessionId,
}),
impressionData: Boolean(feature.impressionData),
}));
return resultDefinitions;
}
async getNewFrontendApiFeatures(
token: IApiUser,
context: Context,
): Promise<FrontendApiFeatureSchema[]> {
const client = await this.newClientForFrontendApiToken(token);
const definitions = client.getFeatureToggleDefinitions() || [];
const sessionId =
context.sessionId || crypto.randomBytes(18).toString('hex');
const resultDefinitions = definitions const resultDefinitions = definitions
.filter((feature) => { .filter((feature) => {
const enabled = client.isEnabled(feature.name, { const enabled = client.isEnabled(feature.name, {
@ -170,56 +136,13 @@ export class FrontendApiService {
if (!client) { if (!client) {
client = this.createClientForFrontendApiToken(token); client = this.createClientForFrontendApiToken(token);
this.clients.set(token.secret, client); this.clients.set(token.secret, client);
this.config.eventBus.emit(PROXY_REPOSITORY_CREATED);
}
return client;
}
private async newClientForFrontendApiToken(
token: IApiUser,
): Promise<Unleash> {
FrontendApiService.assertExpectedTokenType(token);
let newClient = this.newClients.get(token.secret);
if (!newClient) {
newClient = this.createNewClientForFrontendApiToken(token);
this.newClients.set(token.secret, newClient);
this.config.eventBus.emit(FRONTEND_API_REPOSITORY_CREATED); this.config.eventBus.emit(FRONTEND_API_REPOSITORY_CREATED);
} }
return newClient;
}
private async createClientForFrontendApiToken(
token: IApiUser,
): Promise<Unleash> {
const repository = new ProxyRepository(
this.config,
this.stores,
this.services,
token,
);
const client = new Unleash({
appName: 'proxy',
url: 'unused',
storageProvider: new InMemStorageProvider(),
disableMetrics: true,
repository,
disableAutoStart: true,
skipInstanceCountWarning: true,
});
client.on(UnleashEvents.Error, (error) => {
this.logger.error('We found an event error', error);
});
await client.start();
return client; return client;
} }
private async createNewClientForFrontendApiToken( private async createClientForFrontendApiToken(
token: IApiUser, token: IApiUser,
): Promise<Unleash> { ): Promise<Unleash> {
const repository = new FrontendApiRepository( const repository = new FrontendApiRepository(
@ -259,6 +182,10 @@ export class FrontendApiService {
this.clients.forEach((promise) => promise.then((c) => c.destroy())); this.clients.forEach((promise) => promise.then((c) => c.destroy()));
} }
refreshData(): Promise<void> {
return this.globalFrontendApiCache.refreshData();
}
private static assertExpectedTokenType({ type }: IApiUser) { private static assertExpectedTokenType({ type }: IApiUser) {
if (!(type === ApiTokenType.FRONTEND || type === ApiTokenType.ADMIN)) { if (!(type === ApiTokenType.FRONTEND || type === ApiTokenType.ADMIN)) {
throw new InvalidTokenError(); throw new InvalidTokenError();

View File

@ -12,18 +12,16 @@ import {
} from '../../types/models/api-token'; } from '../../types/models/api-token';
import { startOfHour } from 'date-fns'; import { startOfHour } from 'date-fns';
import { import {
FEATURE_UPDATED,
type IConstraint, type IConstraint,
type IStrategyConfig, type IStrategyConfig,
SYSTEM_USER_AUDIT, SYSTEM_USER_AUDIT,
TEST_AUDIT_USER, TEST_AUDIT_USER,
} from '../../types'; } from '../../types';
import { ProxyRepository } from './index'; import type { FrontendApiService } from './frontend-api-service';
import type { Logger } from '../../logger';
let app: IUnleashTest; let app: IUnleashTest;
let db: ITestDb; let db: ITestDb;
const TEST_USER_ID = -9999; let frontendApiService: FrontendApiService;
beforeAll(async () => { beforeAll(async () => {
db = await dbInit('frontend_api', getLogger); db = await dbInit('frontend_api', getLogger);
app = await setupAppWithAuth( app = await setupAppWithAuth(
@ -33,6 +31,7 @@ beforeAll(async () => {
}, },
db.rawDatabase, db.rawDatabase,
); );
frontendApiService = app.services.frontendApiService;
}); });
afterEach(() => { afterEach(() => {
@ -161,6 +160,7 @@ test('should allow requests with a token secret alias', async () => {
alias: randomId(), alias: randomId(),
environment: envB, environment: envB,
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
@ -226,6 +226,7 @@ test('should allow requests with an admin token', async () => {
projects: ['*'], projects: ['*'],
environment: '*', environment: '*',
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', adminToken.secret) .set('Authorization', adminToken.secret)
@ -264,6 +265,7 @@ test('should not allow requests with an invalid frontend token', async () => {
test('should allow requests with a frontend token', async () => { test('should allow requests with a frontend token', async () => {
const frontendToken = await createApiToken(ApiTokenType.FRONTEND); const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -441,6 +443,7 @@ test('should filter features by enabled/disabled', async () => {
enabled: false, enabled: false,
strategies: [{ name: 'default', constraints: [], parameters: {} }], strategies: [{ name: 'default', constraints: [], parameters: {} }],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -488,6 +491,7 @@ test('should filter features by strategies', async () => {
enabled: true, enabled: true,
strategies: [{ name: 'default', constraints: [], parameters: {} }], strategies: [{ name: 'default', constraints: [], parameters: {} }],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -544,6 +548,7 @@ test('should filter features by constraints', async () => {
}, },
], ],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend?appName=a') .get('/api/frontend?appName=a')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -585,6 +590,7 @@ test('should be able to set environment as a context variable', async () => {
], ],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend?environment=staging') .get('/api/frontend?environment=staging')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -604,7 +610,6 @@ test('should be able to set environment as a context variable', async () => {
expect(res.body.toggles).toHaveLength(0); expect(res.body.toggles).toHaveLength(0);
}); });
}); });
test('should filter features by project', async () => { test('should filter features by project', async () => {
const projectA = 'projectA'; const projectA = 'projectA';
const projectB = 'projectB'; const projectB = 'projectB';
@ -636,6 +641,7 @@ test('should filter features by project', async () => {
enabled: true, enabled: true,
strategies: [{ name: 'default', parameters: {} }], strategies: [{ name: 'default', parameters: {} }],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendTokenDefault.secret) .set('Authorization', frontendTokenDefault.secret)
@ -768,6 +774,7 @@ test('should filter features by environment', async () => {
enabled: true, enabled: true,
strategies: [{ name: 'default', parameters: {} }], strategies: [{ name: 'default', parameters: {} }],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendTokenEnvironmentDefault.secret) .set('Authorization', frontendTokenEnvironmentDefault.secret)
@ -868,6 +875,7 @@ test('should filter features by segment', async () => {
await app.services.segmentService.addToStrategy(segmentA.id, strategyA.id); await app.services.segmentService.addToStrategy(segmentA.id, strategyA.id);
await app.services.segmentService.addToStrategy(segmentB.id, strategyB.id); await app.services.segmentService.addToStrategy(segmentB.id, strategyB.id);
const frontendToken = await createApiToken(ApiTokenType.FRONTEND); const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -900,102 +908,6 @@ test('should filter features by segment', async () => {
.expect((res) => expect(res.body).toEqual({ toggles: [] })); .expect((res) => expect(res.body).toEqual({ toggles: [] }));
}); });
test('Should sync proxy for keys on an interval', async () => {
jest.useFakeTimers();
const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
const user = await app.services.apiTokenService.getUserForToken(
frontendToken.secret,
);
const spy = jest.spyOn(
ProxyRepository.prototype as any,
'featuresForToken',
);
expect(user).not.toBeNull();
const proxyRepository = new ProxyRepository(
{
getLogger,
frontendApi: { refreshIntervalInMs: 5000 },
eventBus: <any>{ emit: jest.fn() },
},
db.stores,
app.services,
user!,
);
await proxyRepository.start();
jest.advanceTimersByTime(60000);
proxyRepository.stop();
expect(spy.mock.calls.length > 6).toBe(true);
jest.useRealTimers();
});
test('Should change fetch interval', async () => {
jest.useFakeTimers();
const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
const user = await app.services.apiTokenService.getUserForToken(
frontendToken.secret,
);
const spy = jest.spyOn(
ProxyRepository.prototype as any,
'featuresForToken',
);
const proxyRepository = new ProxyRepository(
{
getLogger,
frontendApi: { refreshIntervalInMs: 1000 },
eventBus: <any>{ emit: jest.fn() },
},
db.stores,
app.services,
user!,
);
await proxyRepository.start();
jest.advanceTimersByTime(60000);
proxyRepository.stop();
expect(spy.mock.calls.length > 30).toBe(true);
jest.useRealTimers();
});
test('Should not recursively set off timers on events', async () => {
jest.useFakeTimers();
const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
const user = await app.services.apiTokenService.getUserForToken(
frontendToken.secret,
);
const spy = jest.spyOn(ProxyRepository.prototype as any, 'dataPolling');
const proxyRepository = new ProxyRepository(
{
getLogger,
frontendApi: { refreshIntervalInMs: 5000 },
eventBus: <any>{ emit: jest.fn() },
},
db.stores,
app.services,
user!,
);
await proxyRepository.start();
db.stores.eventStore.emit(FEATURE_UPDATED);
jest.advanceTimersByTime(10000);
proxyRepository.stop();
expect(spy.mock.calls.length < 3).toBe(true);
jest.useRealTimers();
});
test('should return maxAge header on options call', async () => { test('should return maxAge header on options call', async () => {
await app.request await app.request
.options('/api/frontend') .options('/api/frontend')
@ -1006,51 +918,6 @@ test('should return maxAge header on options call', async () => {
}); });
}); });
test('should terminate data polling when stop is called', async () => {
const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
const user = await app.services.apiTokenService.getUserForToken(
frontendToken.secret,
);
const logTrap: any[] = [];
const getDebugLogger = (): Logger => {
return {
/* eslint-disable-next-line */
debug: (message: any, ...args: any[]) => {
logTrap.push(message);
},
/* eslint-disable-next-line */
info: (...args: any[]) => {},
/* eslint-disable-next-line */
warn: (...args: any[]) => {},
/* eslint-disable-next-line */
error: (...args: any[]) => {},
/* eslint-disable-next-line */
fatal: (...args: any[]) => {},
};
};
/* eslint-disable-next-line */
const proxyRepository = new ProxyRepository(
{
getLogger: getDebugLogger,
frontendApi: { refreshIntervalInMs: 1 },
eventBus: <any>{ emit: jest.fn() },
},
db.stores,
app.services,
user!,
);
await proxyRepository.start();
proxyRepository.stop();
// Polling here is an async recursive call, so we gotta give it a bit of time
await new Promise((r) => setTimeout(r, 10));
expect(logTrap).toContain(
'Shutting down data polling for proxy repository',
);
});
test('should evaluate strategies when returning toggles', async () => { test('should evaluate strategies when returning toggles', async () => {
const frontendToken = await createApiToken(ApiTokenType.FRONTEND); const frontendToken = await createApiToken(ApiTokenType.FRONTEND);
await createFeatureToggle({ await createFeatureToggle({
@ -1084,6 +951,7 @@ test('should evaluate strategies when returning toggles', async () => {
], ],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -1144,6 +1012,7 @@ test('should not return all features', async () => {
}, },
], ],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -1256,6 +1125,7 @@ test('should NOT evaluate disabled strategies when returning toggles', async ()
], ],
}); });
await frontendApiService.refreshData();
await app.request await app.request
.get('/api/frontend') .get('/api/frontend')
.set('Authorization', frontendToken.secret) .set('Authorization', frontendToken.secret)
@ -1344,7 +1214,7 @@ test('should resolve variable rollout percentage consistently', async () => {
}, },
], ],
}); });
await frontendApiService.refreshData();
for (let i = 0; i < 10; ++i) { for (let i = 0; i < 10; ++i) {
const { body } = await app.request const { body } = await app.request
.get('/api/frontend') .get('/api/frontend')

View File

@ -110,9 +110,8 @@ export class GlobalFrontendApiCache extends EventEmitter {
} }
// TODO: fetch only relevant projects/environments based on tokens // TODO: fetch only relevant projects/environments based on tokens
private async refreshData() { public async refreshData() {
try { try {
const stopTimer = this.timer('refreshData');
this.featuresByEnvironment = await this.getAllFeatures(); this.featuresByEnvironment = await this.getAllFeatures();
this.segments = await this.getAllSegments(); this.segments = await this.getAllSegments();
if (this.status === 'starting') { if (this.status === 'starting') {
@ -122,7 +121,6 @@ export class GlobalFrontendApiCache extends EventEmitter {
this.status = 'updated'; this.status = 'updated';
this.emit('updated'); this.emit('updated');
} }
stopTimer();
} catch (e) { } catch (e) {
this.logger.error('Cannot load data for token', e); this.logger.error('Cannot load data for token', e);
} }

View File

@ -51,8 +51,6 @@ export type IFlagKey =
| 'responseTimeMetricsFix' | 'responseTimeMetricsFix'
| 'scimApi' | 'scimApi'
| 'displayEdgeBanner' | 'displayEdgeBanner'
| 'globalFrontendApiCache'
| 'returnGlobalFrontendApiCache'
| 'projectOverviewRefactor' | 'projectOverviewRefactor'
| 'variantDependencies' | 'variantDependencies'
| 'disableShowContextFieldSelectionValues' | 'disableShowContextFieldSelectionValues'
@ -255,14 +253,6 @@ const flags: IFlags = {
process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_METRICS_FIX, process.env.UNLEASH_EXPERIMENTAL_RESPONSE_TIME_METRICS_FIX,
false, false,
), ),
globalFrontendApiCache: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_GLOBAL_FRONTEND_API_CACHE,
false,
),
returnGlobalFrontendApiCache: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_RETURN_GLOBAL_FRONTEND_API_CACHE,
false,
),
projectOverviewRefactor: parseEnvVarBoolean( projectOverviewRefactor: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_PROJECT_OVERVIEW_REFACTOR, process.env.UNLEASH_EXPERIMENTAL_PROJECT_OVERVIEW_REFACTOR,
false, false,

View File

@ -47,8 +47,6 @@ process.nextTick(async () => {
executiveDashboardUI: true, executiveDashboardUI: true,
userAccessUIEnabled: true, userAccessUIEnabled: true,
outdatedSdksBanner: true, outdatedSdksBanner: true,
globalFrontendApiCache: true,
returnGlobalFrontendApiCache: false,
projectOverviewRefactor: true, projectOverviewRefactor: true,
disableShowContextFieldSelectionValues: false, disableShowContextFieldSelectionValues: false,
variantDependencies: true, variantDependencies: true,