1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/src/lib/services/proxy-service.ts

200 lines
5.8 KiB
TypeScript
Raw Normal View History

import { IUnleashConfig, IUnleashServices, IUnleashStores } from '../types';
import { Logger } from '../logger';
import { ClientMetricsSchema, ProxyFeatureSchema } from '../openapi';
import ApiUser, { IApiUser } from '../types/api-user';
import {
Context,
InMemStorageProvider,
Unleash,
UnleashEvents,
} from 'unleash-client';
import { ProxyRepository } from '../proxy';
import { ApiTokenType } from '../types/models/api-token';
import {
FrontendSettings,
frontendSettingsKey,
} from '../types/settings/frontend-settings';
import { validateOrigins } from '../util';
import { BadDataError, InvalidTokenError } from '../error';
type Config = Pick<
IUnleashConfig,
'getLogger' | 'frontendApi' | 'frontendApiOrigins'
>;
type Stores = Pick<IUnleashStores, 'projectStore' | 'eventStore'>;
type Services = Pick<
IUnleashServices,
| 'featureToggleServiceV2'
| 'segmentService'
| 'clientMetricsServiceV2'
| 'settingService'
| 'configurationRevisionService'
>;
export class ProxyService {
private readonly config: Config;
private readonly logger: Logger;
private readonly stores: Stores;
private readonly services: Services;
/**
* This is intentionally a Promise becasue we want to be able to await
* 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
*/
private readonly clients: Map<ApiUser['secret'], Promise<Unleash>> =
new Map();
private cachedFrontendSettings?: FrontendSettings;
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: IApiUser,
context: Context,
): Promise<ProxyFeatureSchema[]> {
const client = await this.clientForProxyToken(token);
const definitions = client.getFeatureToggleDefinitions() || [];
const sessionId = context.sessionId || String(Math.random());
return 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),
}));
}
async registerProxyMetrics(
token: IApiUser,
metrics: ClientMetricsSchema,
ip: string,
): Promise<void> {
ProxyService.assertExpectedTokenType(token);
const environment =
this.services.clientMetricsServiceV2.resolveMetricsEnvironment(
token as ApiUser,
metrics,
);
await this.services.clientMetricsServiceV2.registerClientMetrics(
{ ...metrics, environment },
ip,
);
}
private async clientForProxyToken(token: IApiUser): Promise<Unleash> {
ProxyService.assertExpectedTokenType(token);
let client = this.clients.get(token.secret);
if (!client) {
client = this.createClientForProxyToken(token);
this.clients.set(token.secret, client);
}
return client;
}
private async createClientForProxyToken(token: IApiUser): Promise<Unleash> {
const repository = new ProxyRepository(
this.config,
this.stores,
this.services,
token,
);
fix(deps): update dependency unleash-client to v3.18.0 (#2956) [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [unleash-client](https://togithub.com/Unleash/unleash-client-node) | [`3.16.1` -> `3.18.0`](https://renovatebot.com/diffs/npm/unleash-client/3.16.1/3.18.0) | [![age](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/compatibility-slim/3.16.1)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/confidence-slim/3.16.1)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>Unleash/unleash-client-node</summary> ### [`v3.18.0`](https://togithub.com/Unleash/unleash-client-node/blob/HEAD/CHANGELOG.md#&#8203;3180) [Compare Source](https://togithub.com/Unleash/unleash-client-node/compare/v3.17.0...v3.18.0) feat: gracefully handle unsuccessful metrics post ([#&#8203;414](https://togithub.com/Unleash/unleash-client-node/issues/414)) feat/flush metrics ([#&#8203;415](https://togithub.com/Unleash/unleash-client-node/issues/415)) feat: add metrics jitter support ([#&#8203;412](https://togithub.com/Unleash/unleash-client-node/issues/412)) fix: Allow SDK to startup when backup data is corrupt ([#&#8203;418](https://togithub.com/Unleash/unleash-client-node/issues/418)) fix: flexible-rollout random stickiness is not random enough ([#&#8203;417](https://togithub.com/Unleash/unleash-client-node/issues/417)) fix: build correct version on npm version chore(deps): update dependency eslint-plugin-import to v2.27.5 ([#&#8203;416](https://togithub.com/Unleash/unleash-client-node/issues/416)) chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.48.2 ([#&#8203;413](https://togithub.com/Unleash/unleash-client-node/issues/413)) chore(deps): update dependency eslint to v8.32.0 ([#&#8203;410](https://togithub.com/Unleash/unleash-client-node/issues/410)) chore(deps): update dependency prettier to v2.8.3 ([#&#8203;406](https://togithub.com/Unleash/unleash-client-node/issues/406)) chore(deps): update dependency eslint-plugin-import to v2.27.4 ([#&#8203;404](https://togithub.com/Unleash/unleash-client-node/issues/404)) ### [`v3.17.0`](https://togithub.com/Unleash/unleash-client-node/blob/HEAD/CHANGELOG.md#&#8203;3170) [Compare Source](https://togithub.com/Unleash/unleash-client-node/compare/v3.16.1...v3.17.0) - feat: Only initialize the SDK once. ([#&#8203;368](https://togithub.com/Unleash/unleash-client-node/issues/368)) - fix: upgrade semver to 7.3.8 - fix: add resolution for debug - fix: add resolution for minimatch - fix: add resolution for qs - fix: add resolution for json5 - fix: update yarn.lock - docs: Update the readme with info from docs.getunleash ([#&#8203;399](https://togithub.com/Unleash/unleash-client-node/issues/399)) - docs: minor fix in README - chore(deps): update dependency debug to v4 ([#&#8203;402](https://togithub.com/Unleash/unleash-client-node/issues/402)) - chore(deps): update dependency json5 to v2 ([#&#8203;401](https://togithub.com/Unleash/unleash-client-node/issues/401)) - chore(deps): update dependency eslint to v8.31.0 ([#&#8203;394](https://togithub.com/Unleash/unleash-client-node/issues/394)) - chore(deps): update dependency nock to v13.3.0 ([#&#8203;400](https://togithub.com/Unleash/unleash-client-node/issues/400)) - chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.48.1 ([#&#8203;395](https://togithub.com/Unleash/unleash-client-node/issues/395)) - chore(deps): update dependency eslint-config-prettier to v8.6.0 ([#&#8203;396](https://togithub.com/Unleash/unleash-client-node/issues/396)) - chore(deps): update dependency prettier to v2.8.2 ([#&#8203;398](https://togithub.com/Unleash/unleash-client-node/issues/398)) - chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.47.1 ([#&#8203;346](https://togithub.com/Unleash/unleash-client-node/issues/346)) - chore(deps): update dependency typescript to v4.9.4 ([#&#8203;386](https://togithub.com/Unleash/unleash-client-node/issues/386)) - chore(deps): update dependency sinon to v15 ([#&#8203;391](https://togithub.com/Unleash/unleash-client-node/issues/391)) - chore(deps): update dependency [@&#8203;types/node](https://togithub.com/types/node) to v18 ([#&#8203;380](https://togithub.com/Unleash/unleash-client-node/issues/380)) - chore(deps): update dependency [@&#8203;types/node](https://togithub.com/types/node) to v14.18.36 ([#&#8203;382](https://togithub.com/Unleash/unleash-client-node/issues/382)) - chore(deps): update dependency eslint to v8.30.0 ([#&#8203;367](https://togithub.com/Unleash/unleash-client-node/issues/367)) - chore(deps): update dependency prettier to v2.8.1 ([#&#8203;387](https://togithub.com/Unleash/unleash-client-node/issues/387)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/Unleash/unleash). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMDUuNCIsInVwZGF0ZWRJblZlciI6IjM0LjExNy4xIn0=--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ivar Conradi Østhus <ivar@getunleash.ai>
2023-02-10 10:51:53 +01:00
const client = new Unleash({
appName: 'proxy',
url: 'unused',
storageProvider: new InMemStorageProvider(),
disableMetrics: true,
repository,
});
client.on(UnleashEvents.Error, (error) => {
this.logger.error(error);
});
fix(deps): update dependency unleash-client to v3.18.0 (#2956) [![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [unleash-client](https://togithub.com/Unleash/unleash-client-node) | [`3.16.1` -> `3.18.0`](https://renovatebot.com/diffs/npm/unleash-client/3.16.1/3.18.0) | [![age](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/compatibility-slim/3.16.1)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/unleash-client/3.18.0/confidence-slim/3.16.1)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>Unleash/unleash-client-node</summary> ### [`v3.18.0`](https://togithub.com/Unleash/unleash-client-node/blob/HEAD/CHANGELOG.md#&#8203;3180) [Compare Source](https://togithub.com/Unleash/unleash-client-node/compare/v3.17.0...v3.18.0) feat: gracefully handle unsuccessful metrics post ([#&#8203;414](https://togithub.com/Unleash/unleash-client-node/issues/414)) feat/flush metrics ([#&#8203;415](https://togithub.com/Unleash/unleash-client-node/issues/415)) feat: add metrics jitter support ([#&#8203;412](https://togithub.com/Unleash/unleash-client-node/issues/412)) fix: Allow SDK to startup when backup data is corrupt ([#&#8203;418](https://togithub.com/Unleash/unleash-client-node/issues/418)) fix: flexible-rollout random stickiness is not random enough ([#&#8203;417](https://togithub.com/Unleash/unleash-client-node/issues/417)) fix: build correct version on npm version chore(deps): update dependency eslint-plugin-import to v2.27.5 ([#&#8203;416](https://togithub.com/Unleash/unleash-client-node/issues/416)) chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.48.2 ([#&#8203;413](https://togithub.com/Unleash/unleash-client-node/issues/413)) chore(deps): update dependency eslint to v8.32.0 ([#&#8203;410](https://togithub.com/Unleash/unleash-client-node/issues/410)) chore(deps): update dependency prettier to v2.8.3 ([#&#8203;406](https://togithub.com/Unleash/unleash-client-node/issues/406)) chore(deps): update dependency eslint-plugin-import to v2.27.4 ([#&#8203;404](https://togithub.com/Unleash/unleash-client-node/issues/404)) ### [`v3.17.0`](https://togithub.com/Unleash/unleash-client-node/blob/HEAD/CHANGELOG.md#&#8203;3170) [Compare Source](https://togithub.com/Unleash/unleash-client-node/compare/v3.16.1...v3.17.0) - feat: Only initialize the SDK once. ([#&#8203;368](https://togithub.com/Unleash/unleash-client-node/issues/368)) - fix: upgrade semver to 7.3.8 - fix: add resolution for debug - fix: add resolution for minimatch - fix: add resolution for qs - fix: add resolution for json5 - fix: update yarn.lock - docs: Update the readme with info from docs.getunleash ([#&#8203;399](https://togithub.com/Unleash/unleash-client-node/issues/399)) - docs: minor fix in README - chore(deps): update dependency debug to v4 ([#&#8203;402](https://togithub.com/Unleash/unleash-client-node/issues/402)) - chore(deps): update dependency json5 to v2 ([#&#8203;401](https://togithub.com/Unleash/unleash-client-node/issues/401)) - chore(deps): update dependency eslint to v8.31.0 ([#&#8203;394](https://togithub.com/Unleash/unleash-client-node/issues/394)) - chore(deps): update dependency nock to v13.3.0 ([#&#8203;400](https://togithub.com/Unleash/unleash-client-node/issues/400)) - chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.48.1 ([#&#8203;395](https://togithub.com/Unleash/unleash-client-node/issues/395)) - chore(deps): update dependency eslint-config-prettier to v8.6.0 ([#&#8203;396](https://togithub.com/Unleash/unleash-client-node/issues/396)) - chore(deps): update dependency prettier to v2.8.2 ([#&#8203;398](https://togithub.com/Unleash/unleash-client-node/issues/398)) - chore(deps): update dependency [@&#8203;typescript-eslint/eslint-plugin](https://togithub.com/typescript-eslint/eslint-plugin) to v5.47.1 ([#&#8203;346](https://togithub.com/Unleash/unleash-client-node/issues/346)) - chore(deps): update dependency typescript to v4.9.4 ([#&#8203;386](https://togithub.com/Unleash/unleash-client-node/issues/386)) - chore(deps): update dependency sinon to v15 ([#&#8203;391](https://togithub.com/Unleash/unleash-client-node/issues/391)) - chore(deps): update dependency [@&#8203;types/node](https://togithub.com/types/node) to v18 ([#&#8203;380](https://togithub.com/Unleash/unleash-client-node/issues/380)) - chore(deps): update dependency [@&#8203;types/node](https://togithub.com/types/node) to v14.18.36 ([#&#8203;382](https://togithub.com/Unleash/unleash-client-node/issues/382)) - chore(deps): update dependency eslint to v8.30.0 ([#&#8203;367](https://togithub.com/Unleash/unleash-client-node/issues/367)) - chore(deps): update dependency prettier to v2.8.1 ([#&#8203;387](https://togithub.com/Unleash/unleash-client-node/issues/387)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/Unleash/unleash). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xMDUuNCIsInVwZGF0ZWRJblZlciI6IjM0LjExNy4xIn0=--> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Ivar Conradi Østhus <ivar@getunleash.ai>
2023-02-10 10:51:53 +01:00
await client.start();
return client;
}
async deleteClientForProxyToken(secret: string): Promise<void> {
const clientPromise = this.clients.get(secret);
if (clientPromise) {
const client = await clientPromise;
client.destroy();
this.clients.delete(secret);
}
}
stopAll(): void {
this.clients.forEach((promise) => promise.then((c) => c.destroy()));
}
private static assertExpectedTokenType({ type }: IApiUser) {
if (!(type === ApiTokenType.FRONTEND || type === ApiTokenType.ADMIN)) {
throw new InvalidTokenError();
}
}
async setFrontendSettings(
value: FrontendSettings,
createdBy: string,
createdByUserId: number,
): Promise<void> {
const error = validateOrigins(value.frontendApiOrigins);
if (error) {
throw new BadDataError(error);
}
await this.services.settingService.insert(
frontendSettingsKey,
value,
createdBy,
createdByUserId,
false,
);
}
async fetchFrontendSettings(): Promise<FrontendSettings> {
try {
this.cachedFrontendSettings =
await this.services.settingService.get(frontendSettingsKey, {
frontendApiOrigins: this.config.frontendApiOrigins,
});
} catch (error) {
this.logger.debug('Unable to fetch frontend settings');
}
return this.cachedFrontendSettings;
}
async getFrontendSettings(
useCache: boolean = true,
): Promise<FrontendSettings> {
if (useCache && this.cachedFrontendSettings) {
return this.cachedFrontendSettings;
}
return this.fetchFrontendSettings();
}
}