import { ExecutionContext } from '@nestjs/common'; import { CurrencyCode, LanguageCode, Permission } from '@vendure/common/lib/generated-types'; import { ID, JsonCompatible } from '@vendure/common/lib/shared-types'; import { Request } from 'express'; import { TFunction } from 'i18next'; import { ReplicationMode } from 'typeorm'; import { REQUEST_CONTEXT_KEY, REQUEST_CONTEXT_MAP_KEY } from '../../common/constants'; import { CachedSession } from '../../config/session-cache/session-cache-strategy'; import { Channel } from '../../entity/channel/channel.entity'; import { ApiType } from './get-api-type'; export type SerializedRequestContext = { _req?: any; _session: JsonCompatible>; _apiType: ApiType; _channel: JsonCompatible; _languageCode: LanguageCode; _isAuthorized: boolean; _authorizedAsOwnerOnly: boolean; }; /** * This object is used to store the RequestContext on the Express Request object. */ interface RequestContextStore { /** * This is the default RequestContext for the handler. */ default: RequestContext; /** * If a transaction is started, the resulting RequestContext is stored here. * This RequestContext will have a transaction manager attached via the * TRANSACTION_MANAGER_KEY symbol. * * When a transaction is started, the TRANSACTION_MANAGER_KEY symbol is added to the RequestContext * object. This is then detected inside the {@link internal_setRequestContext} function and the * RequestContext object is stored in the RequestContextStore under the withTransactionManager key. */ withTransactionManager?: RequestContext; } interface RequestWithStores extends Request { [REQUEST_CONTEXT_MAP_KEY]?: Map; [REQUEST_CONTEXT_KEY]?: RequestContextStore; } /** * @description * This function is used to set the {@link RequestContext} on the `req` object. This is the underlying * mechanism by which we are able to access the `RequestContext` from different places. * * For example, here is a diagram to show how, in an incoming API request, we are able to store * and retrieve the `RequestContext` in a resolver: * ``` * - query { product } * | * - AuthGuard.canActivate() * | | creates a `RequestContext`, stores it on `req` * | * - product() resolver * | @Ctx() decorator fetching `RequestContext` from `req` * ``` * * We named it this way to discourage usage outside the framework internals. */ export declare function internal_setRequestContext(req: RequestWithStores, ctx: RequestContext, executionContext?: ExecutionContext): void; /** * @description * Gets the {@link RequestContext} from the `req` object. See {@link internal_setRequestContext} * for more details on this mechanism. */ export declare function internal_getRequestContext(req: RequestWithStores, executionContext?: ExecutionContext): RequestContext; /** * @description * The RequestContext holds information relevant to the current request, which may be * required at various points of the stack. * * It is a good practice to inject the RequestContext (using the {@link Ctx} decorator) into * _all_ resolvers & REST handler, and then pass it through to the service layer. * * This allows the service layer to access information about the current user, the active language, * the active Channel, and so on. In addition, the {@link TransactionalConnection} relies on the * presence of the RequestContext object in order to correctly handle per-request database transactions. * * The RequestContext also provides mechanisms for managing the database replication mode via the * `setReplicationMode` method and the `replicationMode` getter. This allows for finer control * over whether database queries within the context should be executed against the master or a replica * database, which can be particularly useful in distributed database environments. * * @example * ```ts * \@Query() * myQuery(\@Ctx() ctx: RequestContext) { * return this.myService.getData(ctx); * } * ``` * * @example * ```ts * \@Query() * myMutation(\@Ctx() ctx: RequestContext) { * ctx.setReplicationMode('master'); * return this.myService.getData(ctx); * } * ``` * @docsCategory request */ export declare class RequestContext { private readonly _languageCode; private readonly _currencyCode; private readonly _channel; private readonly _session?; private readonly _isAuthorized; private readonly _authorizedAsOwnerOnly; private readonly _translationFn; private readonly _apiType; private readonly _req?; private _replicationMode?; /** * @internal */ constructor(options: { req?: Request; apiType: ApiType; channel: Channel; session?: CachedSession; languageCode?: LanguageCode; currencyCode?: CurrencyCode; isAuthorized: boolean; authorizedAsOwnerOnly: boolean; translationFn?: TFunction; }); /** * @description * Creates an "empty" RequestContext object. This is only intended to be used * when a service method must be called outside the normal request-response * cycle, e.g. when programmatically populating data. Usually a better alternative * is to use the {@link RequestContextService} `create()` method, which allows more control * over the resulting RequestContext object. */ static empty(): RequestContext; /** * @description * Creates a new RequestContext object from a serialized object created by the * `serialize()` method. */ static deserialize(ctxObject: SerializedRequestContext): RequestContext; /** * @description * Returns `true` if there is an active Session & User associated with this request, * and that User has **at least one** of the specified permissions on the active Channel. * * This method uses OR logic - it checks if the user has ANY of the given permissions, * not ALL of them. For AND logic, use {@link userHasAllPermissions}. * * @example * ```ts * // Returns true if user has ReadProduct OR ReadCatalog * ctx.userHasPermissions([Permission.ReadProduct, Permission.ReadCatalog]); * ``` */ userHasPermissions(permissions: Permission[]): boolean; /** * @description * Returns `true` if there is an active Session & User associated with this request, * and that User has **all** of the specified permissions on the active Channel. * * This method uses AND logic - it checks if the user has EVERY one of the given permissions. * For OR logic (any permission), use {@link userHasPermissions}. * * @example * ```ts * // Returns true only if user has BOTH ReadProduct AND UpdateProduct * ctx.userHasAllPermissions([Permission.ReadProduct, Permission.UpdateProduct]); * ``` * * @since 3.6.0 */ userHasAllPermissions(permissions: Permission[]): boolean; /** * @description * Serializes the RequestContext object into a JSON-compatible simple object. * This is useful when you need to send a RequestContext object to another * process, e.g. to pass it to the Job Queue via the {@link JobQueueService}. */ serialize(): SerializedRequestContext; /** * @description * Creates a shallow copy of the RequestContext instance. This means that * mutations to the copy itself will not affect the original, but deep mutations * (e.g. copy.channel.code = 'new') *will* also affect the original. */ copy(): RequestContext; /** * @description * The raw Express request object. */ get req(): Request | undefined; /** * @description * Signals which API this request was received by, e.g. `admin` or `shop`. */ get apiType(): ApiType; /** * @description * The active {@link Channel} of this request. */ get channel(): Channel; get channelId(): ID; get languageCode(): LanguageCode; get currencyCode(): CurrencyCode; get session(): CachedSession | undefined; get activeUserId(): ID | undefined; /** * @description * True if the current session is authorized to access the current resolver method. * * @deprecated Use `userHasPermissions()` method instead. */ get isAuthorized(): boolean; /** * @description * True if the current anonymous session is only authorized to operate on entities that * are owned by the current session. */ get authorizedAsOwnerOnly(): boolean; /** * @description * Translate the given i18n key */ translate(key: string, variables?: { [k: string]: any; }): string; /** * Returns true if any element of arr1 appears in arr2. */ private arraysIntersect; /** * The Express "Request" object is huge and contains many circular * references. We will preserve just a subset of the whole, by preserving * only the serializable properties up to 2 levels deep. * @private */ private shallowCloneRequestObject; /** * @description * Sets the replication mode for the current RequestContext. This mode determines whether the operations * within this context should interact with the master database or a replica. Use this method to explicitly * define the replication mode for the context. * * @param mode - The replication mode to be set (e.g., 'master' or 'replica'). */ setReplicationMode(mode: ReplicationMode): void; /** * @description * Gets the current replication mode of the RequestContext. If no replication mode has been set, * it returns `undefined`. This property indicates whether the context is configured to interact with * the master database or a replica. * * @returns The current replication mode, or `undefined` if none is set. */ get replicationMode(): ReplicationMode | undefined; } export {};