UNPKG

@openfeature/nestjs-sdk

Version:
320 lines (313 loc) 12.9 kB
import * as _nestjs_common from '@nestjs/common'; import { ExecutionContext, DynamicModule, NestInterceptor, CallHandler, HttpException } from '@nestjs/common'; import { Provider, Logger, EvaluationContext as EvaluationContext$1, Hook, ServerProviderEvents, EventHandler, JsonValue, FlagValue } from '@openfeature/server-sdk'; export * from '@openfeature/server-sdk'; import { EvaluationContext } from '@openfeature/core'; import { Observable } from 'rxjs'; /** * A factory function for creating an OpenFeature {@link EvaluationContext} from Nest {@link ExecutionContext}. * This can be used e.g. to get header info from an HTTP request or information from a gRPC call. * * Example getting an HTTP header value: * ```typescript * async function(context: ExecutionContext) { * const request = await context.switchToHttp().getRequest(); * * const userId = request.header('x-user-id'); * * if (userId) { * return { * targetingKey: userId, * }; * } * * return undefined; * } * ``` * @param {ExecutionContext} request The {@link ExecutionContext} to get the information from. * @returns {(Promise<EvaluationContext | undefined> | EvaluationContext | undefined)} The {@link EvaluationContext} new. */ type ContextFactory = (request: ExecutionContext) => Promise<EvaluationContext | undefined> | EvaluationContext | undefined; /** * InjectionToken for a {@link ContextFactory}. * @see {@link Inject} */ declare const ContextFactoryToken: unique symbol; /** * OpenFeatureModule is a NestJS wrapper for OpenFeature Server-SDK. */ declare class OpenFeatureModule { static forRoot({ useGlobalInterceptor, ...options }: OpenFeatureModuleOptions): DynamicModule; } /** * Options for the {@link OpenFeatureModule}. */ interface OpenFeatureModuleOptions { /** * The provider to be set as OpenFeature default provider. * @see {@link OpenFeature#setProvider} */ defaultProvider?: Provider; /** * Domain scoped providers to set to OpenFeature. * @see {@link OpenFeature#setProvider} */ providers?: { [domain: string]: Provider; }; /** * Global {@link Logger} for OpenFeature. * @see {@link OpenFeature#setLogger} */ logger?: Logger; /** * Global {@link EvaluationContext} for OpenFeature. * @see {@link OpenFeature#setContext} */ context?: EvaluationContext$1; /** * Global {@link Hook Hooks} for OpenFeature. * @see {@link OpenFeature#addHooks} */ hooks?: Hook[]; /** * Global {@link EventHandler EventHandlers} for OpenFeature. * @see {@link OpenFeature#addHandler} */ handlers?: [ServerProviderEvents, EventHandler][]; /** * The {@link ContextFactory} for creating an {@link EvaluationContext} from Nest {@link ExecutionContext} information. * This could be header values of a request or something similar. * The context is automatically used for all feature flag evaluations during this request. * @see {@link AsyncLocalStorageTransactionContextPropagator} */ contextFactory?: ContextFactory; /** * If set to false, the global {@link EvaluationContextInterceptor} is disabled. * This means that automatic propagation of the {@link EvaluationContext} created by the {@link this#contextFactory} is not working. * * To enable it again for specific routes, the interceptor can be added for specific controllers or request handlers like seen below: * ```typescript * @Controller() * @UseInterceptors(EvaluationContextInterceptor) * export class Controller {} * ``` * @default true */ useGlobalInterceptor?: boolean; } /** * Returns an injection token for a (domain scoped) OpenFeature client. * @param {string} domain The domain of the OpenFeature client. * @returns {Client} The injection token. */ declare function getOpenFeatureClientToken(domain?: string): string; /** * Options for injecting an OpenFeature client into a constructor. */ interface FeatureClientProps { /** * The domain of the OpenFeature client, if a domain scoped client should be used. * @see {@link Client.getBooleanDetails} */ domain?: string; } /** * Injects a feature client into a constructor or property of a class. * @param {FeatureClientProps} [props] The options for injecting the client. * @returns {PropertyDecorator & ParameterDecorator} The decorator function. */ declare const OpenFeatureClient: (props?: FeatureClientProps) => PropertyDecorator & ParameterDecorator; /** * Options for injecting a feature flag into a route handler. */ interface FeatureProps<T extends FlagValue> { /** * The domain of the OpenFeature client, if a domain scoped client should be used. * @see {@link OpenFeature#getClient} */ domain?: string; /** * The key of the feature flag. * @see {@link Client#getBooleanDetails} */ flagKey: string; /** * The default value for the feature flag. * @see {@link Client#getBooleanDetails} */ defaultValue: T; /** * The {@link EvaluationContext} for evaluating the feature flag. * @see {@link OpenFeature#getClient} */ context?: EvaluationContext$1; } /** * Route handler parameter decorator. * * Gets the {@link EvaluationDetails} for given feature flag from a domain scoped or the default OpenFeature * client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}. * * For example: * ```typescript * @Get('/') * public async handleBooleanRequest( * @BooleanFeatureFlag({ flagKey: 'flagName', defaultValue: false }) * feature: Observable<EvaluationDetails<boolean>>, * ) * ``` * @param {FeatureProps<boolean>} options The options for injecting the feature flag. * @returns {ParameterDecorator} */ declare const BooleanFeatureFlag: (...dataOrPipes: (FeatureProps<boolean> | _nestjs_common.PipeTransform<any, any> | _nestjs_common.Type<_nestjs_common.PipeTransform<any, any>>)[]) => ParameterDecorator; /** * Route handler parameter decorator. * * Gets the {@link EvaluationDetails} for given feature flag from a domain scoped or the default OpenFeature * client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}. * * For example: * ```typescript * @Get('/') * public async handleStringRequest( * @StringFeatureFlag({ flagKey: 'flagName', defaultValue: "default" }) * feature: Observable<EvaluationDetails<string>>, * ) * ``` * @param {FeatureProps<string>} options The options for injecting the feature flag. * @returns {ParameterDecorator} */ declare const StringFeatureFlag: (...dataOrPipes: (FeatureProps<string> | _nestjs_common.PipeTransform<any, any> | _nestjs_common.Type<_nestjs_common.PipeTransform<any, any>>)[]) => ParameterDecorator; /** * Route handler parameter decorator. * * Gets the {@link EvaluationDetails} for given feature flag from a domain scoped or the default OpenFeature * client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}. * * For example: * ```typescript * @Get('/') * public async handleNumberRequest( * @NumberFeatureFlag({ flagKey: 'flagName', defaultValue: 0 }) * feature: Observable<EvaluationDetails<number>>, * ) * ``` * @param {FeatureProps<number>} options The options for injecting the feature flag. * @returns {ParameterDecorator} */ declare const NumberFeatureFlag: (...dataOrPipes: (FeatureProps<number> | _nestjs_common.PipeTransform<any, any> | _nestjs_common.Type<_nestjs_common.PipeTransform<any, any>>)[]) => ParameterDecorator; /** * Route handler parameter decorator. * * Gets the {@link EvaluationDetails} for given feature flag from a domain scoped or the default OpenFeature * client and populates the annotated parameter with the {@link EvaluationDetails} wrapped in an {@link Observable}. * * For example: * ```typescript * @Get('/') * public async handleObjectRequest( * @ObjectFeatureFlag({ flagKey: 'flagName', defaultValue: {} }) * feature: Observable<EvaluationDetails<JsonValue>>, * ) * ``` * @param {FeatureProps<JsonValue>} options The options for injecting the feature flag. * @returns {ParameterDecorator} */ declare const ObjectFeatureFlag: (...dataOrPipes: (FeatureProps<JsonValue> | _nestjs_common.PipeTransform<any, any> | _nestjs_common.Type<_nestjs_common.PipeTransform<any, any>>)[]) => ParameterDecorator; /** * NestJS interceptor used in {@link OpenFeatureModule} * to configure flag evaluation context. * * This interceptor is configured globally by default. * If `useGlobalInterceptor` is set to `false` in {@link OpenFeatureModule} it needs to be configured for the specific controllers or routes. * * If just the interceptor class is passed to the `UseInterceptors` like below, the `contextFactory` provided in the {@link OpenFeatureModule} will be injected and used in order to create the context. * ```ts * //route interceptor * @UseInterceptors(EvaluationContextInterceptor) * @Get('/user-info') * getUserInfo(){} * ``` * * A different `contextFactory` can also be provided, but the interceptor instance has to be instantiated like in the following example. * ```ts * //route interceptor * @UseInterceptors(new EvaluationContextInterceptor(<context factory>)) * @Get('/user-info') * getUserInfo(){} * ``` */ declare class EvaluationContextInterceptor implements NestInterceptor { private contextFactory?; constructor(contextFactory?: ContextFactory | undefined); intercept(executionContext: ExecutionContext, next: CallHandler): Promise<Observable<unknown>>; } type RequiredFlag = { flagKey: string; defaultValue?: boolean; }; /** * Options for using one or more Boolean feature flags to control access to a Controller or Route. */ interface RequireFlagsEnabledProps { /** * The key and default value of the feature flag. * @see {@link Client#getBooleanValue} */ flags: RequiredFlag[]; /** * The exception to throw if any of the required feature flags are not enabled. * Defaults to a 404 Not Found exception. * @see {@link HttpException} * @default new NotFoundException(`Cannot ${req.method} ${req.url}`) */ exception?: HttpException; /** * The domain of the OpenFeature client, if a domain scoped client should be used. * @see {@link OpenFeature#getClient} */ domain?: string; /** * The {@link EvaluationContext} for evaluating the feature flag. * @see {@link OpenFeature#setContext} */ context?: EvaluationContext$1; /** * A factory function for creating an OpenFeature {@link EvaluationContext} from Nest {@link ExecutionContext}. * For example, this can be used to get header info from an HTTP request or information from a gRPC call to be used in the {@link EvaluationContext}. * @see {@link ContextFactory} */ contextFactory?: ContextFactory; } /** * Controller or Route permissions handler decorator. * * Requires that the given feature flags are enabled for the request to be processed, else throws an exception. * * For example: * ```typescript * @RequireFlagsEnabled({ * flags: [ // Required, an array of Boolean flags to check, with optional default values (defaults to false) * { flagKey: 'flagName' }, * { flagKey: 'flagName2', defaultValue: true }, * ], * exception: new ForbiddenException(), // Optional, defaults to a 404 Not Found Exception * domain: 'my-domain', // Optional, defaults to the default OpenFeature Client * context: { // Optional, defaults to the global OpenFeature Context * targetingKey: 'user-id', * }, * contextFactory: (context: ExecutionContext) => { // Optional, defaults to the global OpenFeature Context. Takes precedence over the context option. * return { * targetingKey: context.switchToHttp().getRequest().headers['x-user-id'], * }; * }, * }) * @Get('/') * public async handleGetRequest() * ``` * @param {RequireFlagsEnabledProps} props The options for injecting the feature flag. * @returns {ClassDecorator & MethodDecorator} The decorator that can be used to require Boolean Feature Flags to be enabled for a controller or a specific route. */ declare const RequireFlagsEnabled: (props: RequireFlagsEnabledProps) => ClassDecorator & MethodDecorator; export { BooleanFeatureFlag, type ContextFactory, ContextFactoryToken, EvaluationContextInterceptor, NumberFeatureFlag, ObjectFeatureFlag, OpenFeatureClient, OpenFeatureModule, type OpenFeatureModuleOptions, RequireFlagsEnabled, StringFeatureFlag, getOpenFeatureClientToken };