Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(common,core): make HttpServer#applyVersionFilter mandatory #9591

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 2 additions & 6 deletions packages/common/interfaces/http/http-server.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,9 @@ export interface HttpServer<TRequest = any, TResponse = any> {
close(): any;
getType(): string;
init?(): Promise<void>;
applyVersionFilter?(
applyVersionFilter(
handler: Function,
version: VersionValue,
versioningOptions: VersioningOptions,
): <TRequest extends Record<string, any> = any, TResponse = any>(
req: TRequest,
res: TResponse,
next: () => void,
) => any;
): (req: TRequest, res: TResponse, next: () => void) => Function;
}
9 changes: 7 additions & 2 deletions packages/core/adapters/http-adapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HttpServer, RequestMethod } from '@nestjs/common';
import { RequestHandler } from '@nestjs/common/interfaces';
import { HttpServer, RequestMethod, VersioningOptions } from '@nestjs/common';
import { RequestHandler, VersionValue } from '@nestjs/common/interfaces';
import {
CorsOptions,
CorsOptionsDelegate,
Expand Down Expand Up @@ -121,4 +121,9 @@ export abstract class AbstractHttpAdapter<
| ((path: string, callback: Function) => any)
| Promise<(path: string, callback: Function) => any>;
abstract getType(): string;
abstract applyVersionFilter(
handler: Function,
version: VersionValue,
versioningOptions: VersioningOptions,
): (req: TRequest, res: TResponse, next: () => void) => Function;
}
132 changes: 5 additions & 127 deletions packages/core/router/router-explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -332,134 +332,12 @@ export class RouterExplorer {
routePathMetadata: RoutePathMetadata,
handler: Function,
) {
const { versioningOptions } = routePathMetadata;
const version = this.routePathFactory.getVersion(routePathMetadata);
if (router?.applyVersionFilter) {
return router.applyVersionFilter(handler, version, versioningOptions);
}
/**
* TODO(v9): This was left for backward-compatibility and can be removed.
*/
return <TRequest extends Record<string, any> = any, TResponse = any>(
req: TRequest,
res: TResponse,
next: () => void,
) => {
if (version === VERSION_NEUTRAL) {
return handler(req, res, next);
}
// URL Versioning is done via the path, so the filter continues forward
if (versioningOptions.type === VersioningType.URI) {
return handler(req, res, next);
}

// Custom Extractor Versioning Handler
if (versioningOptions.type === VersioningType.CUSTOM) {
const extractedVersion = versioningOptions.extractor(req) as
| string
| string[]
| Array<string | symbol>;

if (Array.isArray(version)) {
if (
Array.isArray(extractedVersion) &&
version.filter(
extractedVersion.includes as (
value: string | symbol,
index: number,
array: Array<string | symbol>,
) => boolean,
).length
) {
return handler(req, res, next);
} else if (
isString(extractedVersion) &&
version.includes(extractedVersion)
) {
return handler(req, res, next);
}
} else {
if (
Array.isArray(extractedVersion) &&
extractedVersion.includes(version)
) {
return handler(req, res, next);
} else if (
isString(extractedVersion) &&
version === extractedVersion
) {
return handler(req, res, next);
}
}
}

// Media Type (Accept Header) Versioning Handler
if (versioningOptions.type === VersioningType.MEDIA_TYPE) {
const MEDIA_TYPE_HEADER = 'Accept';
const acceptHeaderValue: string | undefined =
req.headers?.[MEDIA_TYPE_HEADER] ||
req.headers?.[MEDIA_TYPE_HEADER.toLowerCase()];

const acceptHeaderVersionParameter = acceptHeaderValue
? acceptHeaderValue.split(';')[1]
: undefined;

// No version was supplied
if (isUndefined(acceptHeaderVersionParameter)) {
if (Array.isArray(version)) {
if (version.includes(VERSION_NEUTRAL)) {
return handler(req, res, next);
}
}
} else {
const headerVersion = acceptHeaderVersionParameter.split(
versioningOptions.key,
)[1];

if (Array.isArray(version)) {
if (version.includes(headerVersion)) {
return handler(req, res, next);
}
} else if (isString(version)) {
if (version === headerVersion) {
return handler(req, res, next);
}
}
}
}
// Header Versioning Handler
else if (versioningOptions.type === VersioningType.HEADER) {
const customHeaderVersionParameter: string | undefined =
req.headers?.[versioningOptions.header] ||
req.headers?.[versioningOptions.header.toLowerCase()];

// No version was supplied
if (isUndefined(customHeaderVersionParameter)) {
if (Array.isArray(version)) {
if (version.includes(VERSION_NEUTRAL)) {
return handler(req, res, next);
}
}
} else {
if (Array.isArray(version)) {
if (version.includes(customHeaderVersionParameter)) {
return handler(req, res, next);
}
} else if (isString(version)) {
if (version === customHeaderVersionParameter) {
return handler(req, res, next);
}
}
}
}

if (!next) {
throw new InternalServerErrorException(
'HTTP adapter does not support filtering on version',
);
}
return next();
};
return router.applyVersionFilter(
handler,
version,
routePathMetadata.versioningOptions,
);
}

private createCallbackProxy(
Expand Down