2022-08-16 15:33:33 +02:00
|
|
|
import { IUnleashConfig } from '../types/option';
|
|
|
|
import { Logger } from '../logger';
|
|
|
|
import { IUnleashServices, IUnleashStores } from '../types';
|
|
|
|
import { ProxyFeatureSchema } from '../openapi/spec/proxy-feature-schema';
|
|
|
|
import ApiUser from '../types/api-user';
|
|
|
|
import {
|
|
|
|
Context,
|
|
|
|
InMemStorageProvider,
|
|
|
|
startUnleash,
|
|
|
|
Unleash,
|
|
|
|
UnleashEvents,
|
|
|
|
} from 'unleash-client';
|
|
|
|
import { ProxyRepository } from '../proxy/proxy-repository';
|
|
|
|
import assert from 'assert';
|
|
|
|
import { ApiTokenType } from '../types/models/api-token';
|
|
|
|
import { ProxyMetricsSchema } from '../openapi/spec/proxy-metrics-schema';
|
|
|
|
|
2022-09-28 14:23:41 +02:00
|
|
|
type Config = Pick<IUnleashConfig, 'getLogger' | 'frontendApi'>;
|
2022-08-16 15:33:33 +02:00
|
|
|
|
|
|
|
type Stores = Pick<IUnleashStores, 'projectStore' | 'eventStore'>;
|
|
|
|
|
|
|
|
type Services = Pick<
|
|
|
|
IUnleashServices,
|
|
|
|
'featureToggleServiceV2' | 'segmentService' | 'clientMetricsServiceV2'
|
|
|
|
>;
|
|
|
|
|
|
|
|
export class ProxyService {
|
|
|
|
private readonly config: Config;
|
|
|
|
|
|
|
|
private readonly logger: Logger;
|
|
|
|
|
|
|
|
private readonly stores: Stores;
|
|
|
|
|
|
|
|
private readonly services: Services;
|
|
|
|
|
|
|
|
private readonly clients: Map<ApiUser['secret'], Unleash> = new Map();
|
|
|
|
|
|
|
|
constructor(config: Config, stores: Stores, services: Services) {
|
|
|
|
this.config = config;
|
|
|
|
this.logger = config.getLogger('services/proxy-service.ts');
|
|
|
|
this.stores = stores;
|
|
|
|
this.services = services;
|
|
|
|
}
|
|
|
|
|
|
|
|
async getProxyFeatures(
|
|
|
|
token: ApiUser,
|
|
|
|
context: Context,
|
|
|
|
): Promise<ProxyFeatureSchema[]> {
|
|
|
|
const client = await this.clientForProxyToken(token);
|
|
|
|
const definitions = client.getFeatureToggleDefinitions() || [];
|
|
|
|
|
|
|
|
return definitions
|
|
|
|
.filter((feature) => client.isEnabled(feature.name, context))
|
|
|
|
.map((feature) => ({
|
|
|
|
name: feature.name,
|
|
|
|
enabled: Boolean(feature.enabled),
|
|
|
|
variant: client.forceGetVariant(feature.name, context),
|
|
|
|
impressionData: Boolean(feature.impressionData),
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
async registerProxyMetrics(
|
|
|
|
token: ApiUser,
|
|
|
|
metrics: ProxyMetricsSchema,
|
|
|
|
ip: string,
|
|
|
|
): Promise<void> {
|
|
|
|
ProxyService.assertExpectedTokenType(token);
|
|
|
|
|
|
|
|
const environment =
|
|
|
|
this.services.clientMetricsServiceV2.resolveMetricsEnvironment(
|
|
|
|
token,
|
|
|
|
metrics,
|
|
|
|
);
|
|
|
|
|
|
|
|
await this.services.clientMetricsServiceV2.registerClientMetrics(
|
|
|
|
{ ...metrics, environment },
|
|
|
|
ip,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async clientForProxyToken(token: ApiUser): Promise<Unleash> {
|
|
|
|
ProxyService.assertExpectedTokenType(token);
|
|
|
|
|
|
|
|
if (!this.clients.has(token.secret)) {
|
|
|
|
this.clients.set(
|
|
|
|
token.secret,
|
|
|
|
await this.createClientForProxyToken(token),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.clients.get(token.secret);
|
|
|
|
}
|
|
|
|
|
|
|
|
private async createClientForProxyToken(token: ApiUser): Promise<Unleash> {
|
|
|
|
const repository = new ProxyRepository(
|
|
|
|
this.config,
|
|
|
|
this.stores,
|
|
|
|
this.services,
|
|
|
|
token,
|
|
|
|
);
|
|
|
|
|
|
|
|
const client = await startUnleash({
|
|
|
|
appName: 'proxy',
|
|
|
|
url: 'unused',
|
|
|
|
storageProvider: new InMemStorageProvider(),
|
|
|
|
disableMetrics: true,
|
|
|
|
repository,
|
|
|
|
});
|
|
|
|
|
|
|
|
client.on(UnleashEvents.Error, (error) => {
|
|
|
|
this.logger.error(error);
|
|
|
|
});
|
|
|
|
|
|
|
|
return client;
|
|
|
|
}
|
|
|
|
|
2022-08-22 15:02:39 +02:00
|
|
|
deleteClientForProxyToken(secret: string): void {
|
|
|
|
this.clients.delete(secret);
|
|
|
|
}
|
|
|
|
|
2022-09-28 14:23:41 +02:00
|
|
|
stopAll(): void {
|
|
|
|
this.clients.forEach((client) => client.destroy());
|
|
|
|
}
|
|
|
|
|
2022-08-16 15:33:33 +02:00
|
|
|
private static assertExpectedTokenType({ type }: ApiUser) {
|
2022-08-18 10:20:51 +02:00
|
|
|
assert(type === ApiTokenType.FRONTEND || type === ApiTokenType.ADMIN);
|
2022-08-16 15:33:33 +02:00
|
|
|
}
|
|
|
|
}
|