@openfeature/nestjs-sdk
Version:
OpenFeature Nest.js SDK
320 lines (313 loc) • 12.9 kB
TypeScript
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 };