mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-11 00:08:30 +01:00
feat: Compare old results with new frontend api (#6476)
This commit is contained in:
parent
1949d0134f
commit
8f105f9d30
@ -115,6 +115,7 @@ exports[`should create default config 1`] = `
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"filterInvalidClientMetrics": false,
|
"filterInvalidClientMetrics": false,
|
||||||
|
"globalFrontendApiCache": false,
|
||||||
"googleAuthEnabled": false,
|
"googleAuthEnabled": false,
|
||||||
"inMemoryScheduledChangeRequests": false,
|
"inMemoryScheduledChangeRequests": false,
|
||||||
"increaseUnleashWidth": false,
|
"increaseUnleashWidth": false,
|
||||||
|
@ -4,7 +4,7 @@ import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-mode
|
|||||||
export default class FakeClientFeatureToggleReadModel
|
export default class FakeClientFeatureToggleReadModel
|
||||||
implements IClientFeatureToggleReadModel
|
implements IClientFeatureToggleReadModel
|
||||||
{
|
{
|
||||||
constructor(private value: Record<string, IFeatureToggleClient[]>) {}
|
constructor(private value: Record<string, IFeatureToggleClient[]> = {}) {}
|
||||||
|
|
||||||
getClient(): Promise<Record<string, IFeatureToggleClient[]>> {
|
getClient(): Promise<Record<string, IFeatureToggleClient[]>> {
|
||||||
return Promise.resolve(this.value);
|
return Promise.resolve(this.value);
|
||||||
|
@ -23,20 +23,20 @@ export class FrontendApiRepository
|
|||||||
|
|
||||||
private readonly token: IApiUser;
|
private readonly token: IApiUser;
|
||||||
|
|
||||||
private globalFrontendApiRepository: GlobalFrontendApiCache;
|
private globalFrontendApiCache: GlobalFrontendApiCache;
|
||||||
|
|
||||||
private running: boolean;
|
private running: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: Config,
|
config: Config,
|
||||||
globalFrontendApiRepository: GlobalFrontendApiCache,
|
globalFrontendApiCache: GlobalFrontendApiCache,
|
||||||
token: IApiUser,
|
token: IApiUser,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.logger = config.getLogger('frontend-api-repository.ts');
|
this.logger = config.getLogger('frontend-api-repository.ts');
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.globalFrontendApiRepository = globalFrontendApiRepository;
|
this.globalFrontendApiCache = globalFrontendApiCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTogglesWithSegmentData(): EnhancedFeatureInterface[] {
|
getTogglesWithSegmentData(): EnhancedFeatureInterface[] {
|
||||||
@ -45,18 +45,18 @@ export class FrontendApiRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSegment(id: number): Segment | undefined {
|
getSegment(id: number): Segment | undefined {
|
||||||
return this.globalFrontendApiRepository.getSegment(id);
|
return this.globalFrontendApiCache.getSegment(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getToggle(name: string): FeatureInterface {
|
getToggle(name: string): FeatureInterface {
|
||||||
//@ts-ignore (we must update the node SDK to allow undefined)
|
//@ts-ignore (we must update the node SDK to allow undefined)
|
||||||
return this.globalFrontendApiRepository
|
return this.getToggles(this.token).find(
|
||||||
.getToggles(this.token)
|
(feature) => feature.name === name,
|
||||||
.find((feature) => feature.name);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getToggles(): FeatureInterface[] {
|
getToggles(): FeatureInterface[] {
|
||||||
return this.globalFrontendApiRepository.getToggles(this.token);
|
return this.globalFrontendApiCache.getToggles(this.token);
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(): Promise<void> {
|
async start(): Promise<void> {
|
||||||
|
@ -6,7 +6,12 @@ import noLogger from '../../test/fixtures/no-logger';
|
|||||||
import { FakeSegmentReadModel } from '../features/segment/fake-segment-read-model';
|
import { FakeSegmentReadModel } from '../features/segment/fake-segment-read-model';
|
||||||
import FakeClientFeatureToggleReadModel from './fake-client-feature-toggle-read-model';
|
import FakeClientFeatureToggleReadModel from './fake-client-feature-toggle-read-model';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { IApiUser, IFeatureToggleClient, ISegment } from '../types';
|
import {
|
||||||
|
IApiUser,
|
||||||
|
IFeatureToggleClient,
|
||||||
|
IFlagResolver,
|
||||||
|
ISegment,
|
||||||
|
} from '../types';
|
||||||
import { UPDATE_REVISION } from '../features/feature-toggle/configuration-revision-service';
|
import { UPDATE_REVISION } from '../features/feature-toggle/configuration-revision-service';
|
||||||
|
|
||||||
const state = async (
|
const state = async (
|
||||||
@ -33,11 +38,17 @@ const defaultFeature: IFeatureToggleClient = {
|
|||||||
};
|
};
|
||||||
const defaultSegment = { name: 'segment', id: 1 } as ISegment;
|
const defaultSegment = { name: 'segment', id: 1 } as ISegment;
|
||||||
|
|
||||||
|
const alwaysOnFlagResolver = {
|
||||||
|
isEnabled() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
} as unknown as IFlagResolver;
|
||||||
|
|
||||||
const createCache = (
|
const createCache = (
|
||||||
segment: ISegment = defaultSegment,
|
segment: ISegment = defaultSegment,
|
||||||
features: Record<string, IFeatureToggleClient[]> = {},
|
features: Record<string, IFeatureToggleClient[]> = {},
|
||||||
) => {
|
) => {
|
||||||
const config = { getLogger: noLogger };
|
const config = { getLogger: noLogger, flagResolver: alwaysOnFlagResolver };
|
||||||
const segmentReadModel = new FakeSegmentReadModel([segment as ISegment]);
|
const segmentReadModel = new FakeSegmentReadModel([segment as ISegment]);
|
||||||
const clientFeatureToggleReadModel = new FakeClientFeatureToggleReadModel(
|
const clientFeatureToggleReadModel = new FakeClientFeatureToggleReadModel(
|
||||||
features,
|
features,
|
||||||
|
@ -13,7 +13,7 @@ import { UPDATE_REVISION } from '../features/feature-toggle/configuration-revisi
|
|||||||
import { mapValues } from '../util';
|
import { mapValues } from '../util';
|
||||||
import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-model-type';
|
import { IClientFeatureToggleReadModel } from './client-feature-toggle-read-model-type';
|
||||||
|
|
||||||
type Config = Pick<IUnleashConfig, 'getLogger'>;
|
type Config = Pick<IUnleashConfig, 'getLogger' | 'flagResolver'>;
|
||||||
|
|
||||||
export type GlobalFrontendApiCacheState = 'starting' | 'ready' | 'updated';
|
export type GlobalFrontendApiCacheState = 'starting' | 'ready' | 'updated';
|
||||||
|
|
||||||
@ -103,7 +103,9 @@ export class GlobalFrontendApiCache extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async onUpdateRevisionEvent() {
|
private async onUpdateRevisionEvent() {
|
||||||
await this.refreshData();
|
if (this.config.flagResolver.isEnabled('globalFrontendApiCache')) {
|
||||||
|
await this.refreshData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private environmentNameForToken(token: IApiUser): string {
|
private environmentNameForToken(token: IApiUser): string {
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
emptyResponse,
|
emptyResponse,
|
||||||
getStandardResponses,
|
getStandardResponses,
|
||||||
ProxyClientSchema,
|
ProxyClientSchema,
|
||||||
|
ProxyFeatureSchema,
|
||||||
proxyFeaturesSchema,
|
proxyFeaturesSchema,
|
||||||
ProxyFeaturesSchema,
|
ProxyFeaturesSchema,
|
||||||
} from '../../openapi';
|
} from '../../openapi';
|
||||||
@ -20,6 +21,7 @@ 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';
|
||||||
|
|
||||||
interface ApiUserRequest<
|
interface ApiUserRequest<
|
||||||
PARAM = any,
|
PARAM = any,
|
||||||
@ -173,10 +175,32 @@ export default class FrontendAPIController extends Controller {
|
|||||||
if (!this.config.flagResolver.isEnabled('embedProxy')) {
|
if (!this.config.flagResolver.isEnabled('embedProxy')) {
|
||||||
throw new NotFoundError();
|
throw new NotFoundError();
|
||||||
}
|
}
|
||||||
const toggles = await this.services.proxyService.getProxyFeatures(
|
let toggles: ProxyFeatureSchema[];
|
||||||
req.user,
|
let newToggles: ProxyFeatureSchema[];
|
||||||
FrontendAPIController.createContext(req),
|
if (this.config.flagResolver.isEnabled('globalFrontendApiCache')) {
|
||||||
);
|
[toggles, newToggles] = await Promise.all([
|
||||||
|
this.services.proxyService.getProxyFeatures(
|
||||||
|
req.user,
|
||||||
|
FrontendAPIController.createContext(req),
|
||||||
|
),
|
||||||
|
this.services.proxyService.getNewProxyFeatures(
|
||||||
|
req.user,
|
||||||
|
FrontendAPIController.createContext(req),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
if (!isEqual(toggles, newToggles)) {
|
||||||
|
this.logger.warn(
|
||||||
|
'old features and new feature are different',
|
||||||
|
toggles.length,
|
||||||
|
newToggles.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toggles = await this.services.proxyService.getProxyFeatures(
|
||||||
|
req.user,
|
||||||
|
FrontendAPIController.createContext(req),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
res.set('Cache-control', 'no-cache');
|
res.set('Cache-control', 'no-cache');
|
||||||
|
|
||||||
|
@ -111,6 +111,9 @@ import {
|
|||||||
import { InactiveUsersService } from '../users/inactive/inactive-users-service';
|
import { InactiveUsersService } from '../users/inactive/inactive-users-service';
|
||||||
import { SegmentReadModel } from '../features/segment/segment-read-model';
|
import { SegmentReadModel } from '../features/segment/segment-read-model';
|
||||||
import { FakeSegmentReadModel } from '../features/segment/fake-segment-read-model';
|
import { FakeSegmentReadModel } from '../features/segment/fake-segment-read-model';
|
||||||
|
import { GlobalFrontendApiCache } from '../proxy/global-frontend-api-cache';
|
||||||
|
import ClientFeatureToggleReadModel from '../proxy/client-feature-toggle-read-model';
|
||||||
|
import FakeClientFeatureToggleReadModel from '../proxy/fake-client-feature-toggle-read-model';
|
||||||
|
|
||||||
export const createServices = (
|
export const createServices = (
|
||||||
stores: IUnleashStores,
|
stores: IUnleashStores,
|
||||||
@ -293,12 +296,27 @@ export const createServices = (
|
|||||||
? createClientFeatureToggleService(db, config)
|
? createClientFeatureToggleService(db, config)
|
||||||
: createFakeClientFeatureToggleService(config);
|
: createFakeClientFeatureToggleService(config);
|
||||||
|
|
||||||
const proxyService = new ProxyService(config, stores, {
|
const clientFeatureToggleReadModel = db
|
||||||
featureToggleServiceV2,
|
? new ClientFeatureToggleReadModel(db, config.eventBus)
|
||||||
clientMetricsServiceV2,
|
: new FakeClientFeatureToggleReadModel();
|
||||||
settingService,
|
const globalFrontendApiCache = new GlobalFrontendApiCache(
|
||||||
|
config,
|
||||||
|
segmentReadModel,
|
||||||
|
clientFeatureToggleReadModel,
|
||||||
configurationRevisionService,
|
configurationRevisionService,
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const proxyService = new ProxyService(
|
||||||
|
config,
|
||||||
|
stores,
|
||||||
|
{
|
||||||
|
featureToggleServiceV2,
|
||||||
|
clientMetricsServiceV2,
|
||||||
|
settingService,
|
||||||
|
configurationRevisionService,
|
||||||
|
},
|
||||||
|
globalFrontendApiCache,
|
||||||
|
);
|
||||||
|
|
||||||
const edgeService = new EdgeService({ apiTokenService }, config);
|
const edgeService = new EdgeService({ apiTokenService }, config);
|
||||||
|
|
||||||
|
54
src/lib/services/proxy-service.test.ts
Normal file
54
src/lib/services/proxy-service.test.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ProxyService, Config } from './proxy-service';
|
||||||
|
import { GlobalFrontendApiCache } from '../proxy/global-frontend-api-cache';
|
||||||
|
import { IApiUser } from '../types';
|
||||||
|
import { FeatureInterface } from 'unleash-client/lib/feature';
|
||||||
|
import noLogger from '../../test/fixtures/no-logger';
|
||||||
|
import { ApiTokenType } from '../types/models/api-token';
|
||||||
|
|
||||||
|
test('proxy service fetching features from global cache', async () => {
|
||||||
|
const irrelevant = {} as any;
|
||||||
|
const globalFrontendApiCache = {
|
||||||
|
getToggles(_: IApiUser): FeatureInterface[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'toggleA',
|
||||||
|
enabled: true,
|
||||||
|
project: 'projectA',
|
||||||
|
type: 'release',
|
||||||
|
variants: [],
|
||||||
|
strategies: [
|
||||||
|
{ name: 'default', parameters: [], constraints: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'toggleB',
|
||||||
|
enabled: false,
|
||||||
|
project: 'projectA',
|
||||||
|
type: 'release',
|
||||||
|
variants: [],
|
||||||
|
strategies: [
|
||||||
|
{ name: 'default', parameters: [], constraints: [] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
} as GlobalFrontendApiCache;
|
||||||
|
const proxyService = new ProxyService(
|
||||||
|
{ getLogger: noLogger } as unknown as Config,
|
||||||
|
irrelevant,
|
||||||
|
irrelevant,
|
||||||
|
globalFrontendApiCache,
|
||||||
|
);
|
||||||
|
|
||||||
|
const features = await proxyService.getNewProxyFeatures(
|
||||||
|
{
|
||||||
|
projects: ['irrelevant'],
|
||||||
|
environment: 'irrelevant',
|
||||||
|
type: ApiTokenType.FRONTEND,
|
||||||
|
} as unknown as IApiUser,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(features).toMatchObject([{ name: 'toggleA' }]);
|
||||||
|
expect(features).toHaveLength(1);
|
||||||
|
});
|
@ -17,18 +17,20 @@ import { validateOrigins } from '../util';
|
|||||||
import { BadDataError, InvalidTokenError } from '../error';
|
import { BadDataError, InvalidTokenError } from '../error';
|
||||||
import { PROXY_REPOSITORY_CREATED } from '../metric-events';
|
import { PROXY_REPOSITORY_CREATED } from '../metric-events';
|
||||||
import { ProxyRepository } from '../proxy';
|
import { ProxyRepository } from '../proxy';
|
||||||
|
import { FrontendApiRepository } from '../proxy/frontend-api-repository';
|
||||||
|
import { GlobalFrontendApiCache } from '../proxy/global-frontend-api-cache';
|
||||||
|
|
||||||
type Config = Pick<
|
export type Config = Pick<
|
||||||
IUnleashConfig,
|
IUnleashConfig,
|
||||||
'getLogger' | 'frontendApi' | 'frontendApiOrigins' | 'eventBus'
|
'getLogger' | 'frontendApi' | 'frontendApiOrigins' | 'eventBus'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type Stores = Pick<
|
export type Stores = Pick<
|
||||||
IUnleashStores,
|
IUnleashStores,
|
||||||
'projectStore' | 'eventStore' | 'segmentReadModel'
|
'projectStore' | 'eventStore' | 'segmentReadModel'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type Services = Pick<
|
export type Services = Pick<
|
||||||
IUnleashServices,
|
IUnleashServices,
|
||||||
| 'featureToggleServiceV2'
|
| 'featureToggleServiceV2'
|
||||||
| 'clientMetricsServiceV2'
|
| 'clientMetricsServiceV2'
|
||||||
@ -45,21 +47,31 @@ export class ProxyService {
|
|||||||
|
|
||||||
private readonly services: Services;
|
private readonly services: Services;
|
||||||
|
|
||||||
|
private readonly globalFrontendApiCache: GlobalFrontendApiCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is intentionally a Promise becasue we want to be able to await
|
* This is intentionally a Promise because we want to be able to await
|
||||||
* until the client (which might be being created by a different request) is ready
|
* until the client (which might be being created by a different request) is ready
|
||||||
* Check this test that fails if we don't use a Promise: src/test/e2e/api/proxy/proxy.concurrency.e2e.test.ts
|
* Check this test that fails if we don't use a Promise: src/test/e2e/api/proxy/proxy.concurrency.e2e.test.ts
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
|
||||||
constructor(config: Config, stores: Stores, services: Services) {
|
constructor(
|
||||||
|
config: Config,
|
||||||
|
stores: Stores,
|
||||||
|
services: Services,
|
||||||
|
globalFrontendApiCache: GlobalFrontendApiCache,
|
||||||
|
) {
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.logger = config.getLogger('services/proxy-service.ts');
|
this.logger = config.getLogger('services/proxy-service.ts');
|
||||||
this.stores = stores;
|
this.stores = stores;
|
||||||
this.services = services;
|
this.services = services;
|
||||||
|
this.globalFrontendApiCache = globalFrontendApiCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProxyFeatures(
|
async getProxyFeatures(
|
||||||
@ -85,6 +97,33 @@ export class ProxyService {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getNewProxyFeatures(
|
||||||
|
token: IApiUser,
|
||||||
|
context: Context,
|
||||||
|
): Promise<ProxyFeatureSchema[]> {
|
||||||
|
const client = await this.newClientForProxyToken(token);
|
||||||
|
const definitions = client.getFeatureToggleDefinitions() || [];
|
||||||
|
const sessionId = context.sessionId || String(Math.random());
|
||||||
|
|
||||||
|
return definitions
|
||||||
|
.filter((feature) => {
|
||||||
|
const enabled = client.isEnabled(feature.name, {
|
||||||
|
...context,
|
||||||
|
sessionId,
|
||||||
|
});
|
||||||
|
return enabled;
|
||||||
|
})
|
||||||
|
.map((feature) => ({
|
||||||
|
name: feature.name,
|
||||||
|
enabled: Boolean(feature.enabled),
|
||||||
|
variant: client.getVariant(feature.name, {
|
||||||
|
...context,
|
||||||
|
sessionId,
|
||||||
|
}),
|
||||||
|
impressionData: Boolean(feature.impressionData),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async registerProxyMetrics(
|
async registerProxyMetrics(
|
||||||
token: IApiUser,
|
token: IApiUser,
|
||||||
metrics: ClientMetricsSchema,
|
metrics: ClientMetricsSchema,
|
||||||
@ -117,6 +156,20 @@ export class ProxyService {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async newClientForProxyToken(token: IApiUser): Promise<Unleash> {
|
||||||
|
ProxyService.assertExpectedTokenType(token);
|
||||||
|
|
||||||
|
let newClient = this.newClients.get(token.secret);
|
||||||
|
if (!newClient) {
|
||||||
|
newClient = this.createNewClientForProxyToken(token);
|
||||||
|
this.newClients.set(token.secret, newClient);
|
||||||
|
// TODO: do we need this twice?
|
||||||
|
// this.config.eventBus.emit(PROXY_REPOSITORY_CREATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newClient;
|
||||||
|
}
|
||||||
|
|
||||||
private async createClientForProxyToken(token: IApiUser): Promise<Unleash> {
|
private async createClientForProxyToken(token: IApiUser): Promise<Unleash> {
|
||||||
const repository = new ProxyRepository(
|
const repository = new ProxyRepository(
|
||||||
this.config,
|
this.config,
|
||||||
@ -143,6 +196,33 @@ export class ProxyService {
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async createNewClientForProxyToken(
|
||||||
|
token: IApiUser,
|
||||||
|
): Promise<Unleash> {
|
||||||
|
const repository = new FrontendApiRepository(
|
||||||
|
this.config,
|
||||||
|
this.globalFrontendApiCache,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
async deleteClientForProxyToken(secret: string): Promise<void> {
|
async deleteClientForProxyToken(secret: string): Promise<void> {
|
||||||
const clientPromise = this.clients.get(secret);
|
const clientPromise = this.clients.get(secret);
|
||||||
if (clientPromise) {
|
if (clientPromise) {
|
||||||
|
@ -53,7 +53,8 @@ export type IFlagKey =
|
|||||||
| 'sdkReporting'
|
| 'sdkReporting'
|
||||||
| 'responseTimeMetricsFix'
|
| 'responseTimeMetricsFix'
|
||||||
| 'scimApi'
|
| 'scimApi'
|
||||||
| 'displayEdgeBanner';
|
| 'displayEdgeBanner'
|
||||||
|
| 'globalFrontendApiCache';
|
||||||
|
|
||||||
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>;
|
||||||
|
|
||||||
@ -262,6 +263,10 @@ 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,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultExperimentalOptions: IExperimentalOptions = {
|
export const defaultExperimentalOptions: IExperimentalOptions = {
|
||||||
|
@ -50,6 +50,7 @@ process.nextTick(async () => {
|
|||||||
executiveDashboard: true,
|
executiveDashboard: true,
|
||||||
userAccessUIEnabled: true,
|
userAccessUIEnabled: true,
|
||||||
sdkReporting: true,
|
sdkReporting: true,
|
||||||
|
globalFrontendApiCache: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
authentication: {
|
authentication: {
|
||||||
|
Loading…
Reference in New Issue
Block a user