mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
fix(): fix middleware tests, fix socketio deprecations
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as io from 'socket.io-client';
|
||||
import { io } from 'socket.io-client';
|
||||
import { ErrorGateway } from '../src/error.gateway';
|
||||
|
||||
describe('ErrorGateway', () => {
|
||||
@@ -16,7 +16,7 @@ describe('ErrorGateway', () => {
|
||||
});
|
||||
|
||||
it(`should handle error`, async () => {
|
||||
const ws = io.connect('http://localhost:8080');
|
||||
const ws = io('http://localhost:8080');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as io from 'socket.io-client';
|
||||
import { io } from 'socket.io-client';
|
||||
import { AckGateway } from '../src/ack.gateway';
|
||||
|
||||
async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
const testingModule = await Test.createTestingModule({
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = await testingModule.createNestApplication();
|
||||
const app = testingModule.createNestApplication();
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('WebSocketGateway (ack)', () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io.connect('http://localhost:8080');
|
||||
ws = io('http://localhost:8080');
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('push', { test: 'test' }, data => {
|
||||
expect(data).to.be.eql('pong');
|
||||
@@ -32,7 +32,7 @@ describe('WebSocketGateway (ack)', () => {
|
||||
app = await createNestApp(AckGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io.connect('http://localhost:8080');
|
||||
ws = io('http://localhost:8080');
|
||||
await new Promise<void>(resolve =>
|
||||
ws.emit('push', data => {
|
||||
expect(data).to.be.eql('pong');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as io from 'socket.io-client';
|
||||
import { io } from 'socket.io-client';
|
||||
import { ApplicationGateway } from '../src/app.gateway';
|
||||
import { NamespaceGateway } from '../src/namespace.gateway';
|
||||
import { ServerGateway } from '../src/server.gateway';
|
||||
@@ -10,7 +10,7 @@ async function createNestApp(...gateways): Promise<INestApplication> {
|
||||
const testingModule = await Test.createTestingModule({
|
||||
providers: gateways,
|
||||
}).compile();
|
||||
const app = await testingModule.createNestApplication();
|
||||
const app = testingModule.createNestApplication();
|
||||
return app;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('WebSocketGateway', () => {
|
||||
app = await createNestApp(ApplicationGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io.connect('http://localhost:8080');
|
||||
ws = io('http://localhost:8080');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
@@ -37,7 +37,7 @@ describe('WebSocketGateway', () => {
|
||||
app = await createNestApp(ServerGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io.connect('http://localhost:3000');
|
||||
ws = io('http://localhost:3000');
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
@@ -53,8 +53,8 @@ describe('WebSocketGateway', () => {
|
||||
app = await createNestApp(ApplicationGateway, NamespaceGateway);
|
||||
await app.listen(3000);
|
||||
|
||||
ws = io.connect('http://localhost:8080');
|
||||
io.connect('http://localhost:8080/test').emit('push', {});
|
||||
ws = io('http://localhost:8080');
|
||||
io('http://localhost:8080/test').emit('push', {});
|
||||
ws.emit('push', {
|
||||
test: 'test',
|
||||
});
|
||||
|
||||
@@ -18,8 +18,10 @@ export class RouterMethodFactory {
|
||||
return target.options;
|
||||
case RequestMethod.HEAD:
|
||||
return target.head;
|
||||
default: {
|
||||
case RequestMethod.GET:
|
||||
return target.get;
|
||||
default: {
|
||||
return target.use;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
} from '@nestjs/common/interfaces/middleware';
|
||||
import { MiddlewareConfiguration } from '@nestjs/common/interfaces/middleware/middleware-configuration.interface';
|
||||
import { iterate } from 'iterare';
|
||||
import { NestContainer } from '../injector';
|
||||
import { RoutesMapper } from './routes-mapper';
|
||||
import { filterMiddleware } from './utils';
|
||||
|
||||
@@ -20,7 +19,6 @@ export class MiddlewareBuilder implements MiddlewareConsumer {
|
||||
constructor(
|
||||
private readonly routesMapper: RoutesMapper,
|
||||
private readonly httpAdapter: HttpServer,
|
||||
private readonly container: NestContainer,
|
||||
) {}
|
||||
|
||||
public apply(
|
||||
|
||||
@@ -91,7 +91,6 @@ export class MiddlewareModule {
|
||||
const middlewareBuilder = new MiddlewareBuilder(
|
||||
this.routesMapper,
|
||||
this.httpAdapter,
|
||||
this.container,
|
||||
);
|
||||
await instance.configure(middlewareBuilder);
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { RequestMethod } from '@nestjs/common';
|
||||
import { MODULE_PATH, PATH_METADATA } from '@nestjs/common/constants';
|
||||
import { RouteInfo, Type } from '@nestjs/common/interfaces';
|
||||
import {
|
||||
@@ -23,10 +22,11 @@ export class RoutesMapper {
|
||||
route: Type<any> | RouteInfo | string,
|
||||
): RouteInfo[] {
|
||||
if (isString(route)) {
|
||||
const defaultRequestMethod = -1;
|
||||
return [
|
||||
{
|
||||
path: addLeadingSlash(route),
|
||||
method: RequestMethod.ALL,
|
||||
method: defaultRequestMethod,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export class RoutesMapper {
|
||||
private getHostModuleOfController(
|
||||
metatype: Type<unknown>,
|
||||
): Module | undefined {
|
||||
if (!metatype?.name) {
|
||||
if (!metatype) {
|
||||
return;
|
||||
}
|
||||
const modulesContainer = this.container.getModules();
|
||||
@@ -95,7 +95,7 @@ export class RoutesMapper {
|
||||
const modules = Array.from(modulesContainer.values()).filter(moduleRef =>
|
||||
moduleRefsSet.has(moduleRef),
|
||||
);
|
||||
return modules.find(({ routes }) => routes.has(metatype.name));
|
||||
return modules.find(({ routes }) => routes.has(metatype));
|
||||
}
|
||||
|
||||
private getModulePath(
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { RequestMethod } from '@nestjs/common';
|
||||
import { HttpServer, RouteInfo, Type } from '@nestjs/common/interfaces';
|
||||
import { isFunction } from '@nestjs/common/utils/shared.utils';
|
||||
import { iterate } from 'iterare';
|
||||
import * as pathToRegexp from 'path-to-regexp';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { iterate } from 'iterare';
|
||||
|
||||
type RouteInfoRegex = RouteInfo & { regex: RegExp };
|
||||
|
||||
@@ -85,7 +85,11 @@ export function isRouteExcluded(
|
||||
: originalUrl;
|
||||
|
||||
const isExcluded = excludedRoutes.some(({ method, regex }) => {
|
||||
if (RequestMethod.ALL === method || RequestMethod[method] === reqMethod) {
|
||||
if (
|
||||
RequestMethod.ALL === method ||
|
||||
RequestMethod[method] === reqMethod ||
|
||||
method === -1
|
||||
) {
|
||||
return regex.exec(pathname);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -29,5 +29,6 @@ describe('RouterMethodFactory', () => {
|
||||
expect(factory.get(target, RequestMethod.PATCH)).to.equal(target.patch);
|
||||
expect(factory.get(target, RequestMethod.OPTIONS)).to.equal(target.options);
|
||||
expect(factory.get(target, RequestMethod.HEAD)).to.equal(target.head);
|
||||
expect(factory.get(target, -1)).to.equal(target.use);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { RequestMethod } from '@nestjs/common';
|
||||
import { expect } from 'chai';
|
||||
import { Controller, Get } from '../../../common';
|
||||
import { NestContainer } from '../../injector/container';
|
||||
@@ -14,7 +13,6 @@ describe('MiddlewareBuilder', () => {
|
||||
builder = new MiddlewareBuilder(
|
||||
new RoutesMapper(container),
|
||||
new NoopHttpAdapter({}),
|
||||
container,
|
||||
);
|
||||
});
|
||||
describe('apply', () => {
|
||||
@@ -65,7 +63,7 @@ describe('MiddlewareBuilder', () => {
|
||||
expect(proxy.getExcludedRoutes()).to.be.eql([
|
||||
{
|
||||
path,
|
||||
method: RequestMethod.ALL,
|
||||
method: -1,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -75,7 +75,6 @@ describe('MiddlewareModule', () => {
|
||||
new MiddlewareBuilder(
|
||||
(middlewareModule as any).routesMapper,
|
||||
undefined,
|
||||
stubContainer,
|
||||
),
|
||||
),
|
||||
).to.be.true;
|
||||
|
||||
@@ -312,7 +312,8 @@ export class FastifyAdapter<
|
||||
}
|
||||
if (
|
||||
requestMethod === RequestMethod.ALL ||
|
||||
req.method === RequestMethod[requestMethod]
|
||||
req.method === RequestMethod[requestMethod] ||
|
||||
requestMethod === -1
|
||||
) {
|
||||
return callback(req, res, next);
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import {
|
||||
import { DISCONNECT_EVENT } from '@nestjs/websockets/constants';
|
||||
import { fromEvent, Observable } from 'rxjs';
|
||||
import { filter, first, map, mergeMap, share, takeUntil } from 'rxjs/operators';
|
||||
import * as io from 'socket.io';
|
||||
import { Server, ServerOptions, Socket } from 'socket.io';
|
||||
|
||||
export class IoAdapter extends AbstractWsAdapter {
|
||||
public create(
|
||||
port: number,
|
||||
options?: any & { namespace?: string; server?: any },
|
||||
): any {
|
||||
options?: ServerOptions & { namespace?: string; server?: any },
|
||||
): Server {
|
||||
if (!options) {
|
||||
return this.createIOServer(port);
|
||||
}
|
||||
@@ -26,23 +26,23 @@ export class IoAdapter extends AbstractWsAdapter {
|
||||
|
||||
public createIOServer(port: number, options?: any): any {
|
||||
if (this.httpServer && port === 0) {
|
||||
return io(this.httpServer, options);
|
||||
return new Server(this.httpServer, options);
|
||||
}
|
||||
return io(port, options);
|
||||
return new Server(port, options);
|
||||
}
|
||||
|
||||
public bindMessageHandlers(
|
||||
client: any,
|
||||
socket: Socket,
|
||||
handlers: MessageMappingProperties[],
|
||||
transform: (data: any) => Observable<any>,
|
||||
) {
|
||||
const disconnect$ = fromEvent(client, DISCONNECT_EVENT).pipe(
|
||||
const disconnect$ = fromEvent(socket, DISCONNECT_EVENT).pipe(
|
||||
share(),
|
||||
first(),
|
||||
);
|
||||
|
||||
handlers.forEach(({ message, callback }) => {
|
||||
const source$ = fromEvent(client, message).pipe(
|
||||
const source$ = fromEvent(socket, message).pipe(
|
||||
mergeMap((payload: any) => {
|
||||
const { data, ack } = this.mapPayload(payload);
|
||||
return transform(callback(data, ack)).pipe(
|
||||
@@ -54,17 +54,17 @@ export class IoAdapter extends AbstractWsAdapter {
|
||||
);
|
||||
source$.subscribe(([response, ack]) => {
|
||||
if (response.event) {
|
||||
return client.emit(response.event, response.data);
|
||||
return socket.emit(response.event, response.data);
|
||||
}
|
||||
isFunction(ack) && ack(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public mapPayload(payload: any): { data: any; ack?: Function } {
|
||||
public mapPayload(payload: unknown): { data: any; ack?: Function } {
|
||||
if (!Array.isArray(payload)) {
|
||||
if (isFunction(payload)) {
|
||||
return { data: undefined, ack: payload };
|
||||
return { data: undefined, ack: payload as Function };
|
||||
}
|
||||
return { data: payload };
|
||||
}
|
||||
|
||||
@@ -1,97 +1,122 @@
|
||||
/**
|
||||
* @external https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/socket.io/index.d.ts
|
||||
* @external https://github.com/socketio/socket.io/blob/master/lib/index.ts
|
||||
*/
|
||||
|
||||
import { CorsOptions } from '@nestjs/common/interfaces/external/cors-options.interface';
|
||||
|
||||
export interface GatewayMetadata {
|
||||
/**
|
||||
* The name of a namespace
|
||||
*/
|
||||
namespace?: string | RegExp;
|
||||
|
||||
/**
|
||||
* The path to ws
|
||||
* @default '/socket.io'
|
||||
* Name of the path to capture
|
||||
* @default "/socket.io"
|
||||
*/
|
||||
path?: string;
|
||||
|
||||
/**
|
||||
* Should we serve the client file?
|
||||
* Whether to serve the client files
|
||||
* @default true
|
||||
*/
|
||||
serveClient?: boolean;
|
||||
|
||||
/**
|
||||
* The adapter to use for handling rooms. NOTE: this should be a class,
|
||||
* not an object
|
||||
* @default typeof Adapter
|
||||
* The adapter to use
|
||||
* @default the in-memory adapter (https://github.com/socketio/socket.io-adapter)
|
||||
*/
|
||||
adapter?: any;
|
||||
|
||||
/**
|
||||
* Accepted origins
|
||||
* @default '*:*'
|
||||
* The parser to use
|
||||
* @default the default parser (https://github.com/socketio/socket.io-parser)
|
||||
*/
|
||||
origins?: string;
|
||||
|
||||
parser?: any;
|
||||
|
||||
/**
|
||||
* How many milliseconds without a pong packed to consider the connection closed (engine.io)
|
||||
* @default 60000
|
||||
* How many ms before a client without namespace is closed
|
||||
* @default 45000
|
||||
*/
|
||||
connectTimeout?: number;
|
||||
/**
|
||||
* How many ms without a pong packet to consider the connection closed
|
||||
* @default 5000
|
||||
*/
|
||||
pingTimeout?: number;
|
||||
|
||||
/**
|
||||
* How many milliseconds before sending a new ping packet (keep-alive) (engine.io)
|
||||
* How many ms before sending a new ping packet
|
||||
* @default 25000
|
||||
*/
|
||||
pingInterval?: number;
|
||||
|
||||
/**
|
||||
* How many bytes or characters a message can be when polling, before closing the session
|
||||
* (to avoid Dos) (engine.io)
|
||||
* @default 10E7
|
||||
* How many ms before an uncompleted transport upgrade is cancelled
|
||||
* @default 10000
|
||||
*/
|
||||
upgradeTimeout?: number;
|
||||
/**
|
||||
* How many bytes or characters a message can be, before closing the session (to avoid DoS).
|
||||
* @default 1e5 (100 KB)
|
||||
*/
|
||||
maxHttpBufferSize?: number;
|
||||
|
||||
/**
|
||||
* Transports to allow connections to (engine.io)
|
||||
* @default ['polling','websocket']
|
||||
* A function that receives a given handshake or upgrade request as its first parameter,
|
||||
* and can decide whether to continue or not. The second argument is a function that needs
|
||||
* to be called with the decided information: fn(err, success), where success is a boolean
|
||||
* value where false means that the request is rejected, and err is an error code.
|
||||
*/
|
||||
transports?: string[];
|
||||
|
||||
allowRequest?: (
|
||||
req: any,
|
||||
fn: (err: string | null | undefined, success: boolean) => void,
|
||||
) => void;
|
||||
/**
|
||||
* Whether to allow transport upgrades (engine.io)
|
||||
* The low-level transports that are enabled
|
||||
* @default ["polling", "websocket"]
|
||||
*/
|
||||
transports?: Transport[];
|
||||
/**
|
||||
* Whether to allow transport upgrades
|
||||
* @default true
|
||||
*/
|
||||
allowUpgrades?: boolean;
|
||||
|
||||
/**
|
||||
* parameters of the WebSocket permessage-deflate extension (see ws module).
|
||||
* Set to false to disable (engine.io)
|
||||
* Parameters of the WebSocket permessage-deflate extension (see ws module api docs). Set to false to disable.
|
||||
* @default false
|
||||
*/
|
||||
perMessageDeflate?: boolean | object;
|
||||
/**
|
||||
* Parameters of the http compression for the polling transports (see zlib api docs). Set to false to disable.
|
||||
* @default true
|
||||
*/
|
||||
perMessageDeflate?: Record<string, any> | boolean;
|
||||
|
||||
httpCompression?: boolean | object;
|
||||
/**
|
||||
* Parameters of the http compression for the polling transports (see zlib).
|
||||
* Set to false to disable, or set an object with parameter "threshold:number"
|
||||
* to only compress data if the byte size is above this value (1024) (engine.io)
|
||||
* @default true|1024
|
||||
* What WebSocket server implementation to use. Specified module must
|
||||
* conform to the ws interface (see ws module api docs). Default value is ws.
|
||||
* An alternative c++ addon is also available by installing uws module.
|
||||
*/
|
||||
httpCompression?: Record<string, any> | boolean;
|
||||
|
||||
wsEngine?: string;
|
||||
/**
|
||||
* Name of the HTTP cookie that contains the client sid to send as part of
|
||||
* handshake response headers. Set to false to not send one (engine.io)
|
||||
* @default "io"
|
||||
* An optional packet which will be concatenated to the handshake packet emitted by Engine.IO.
|
||||
*/
|
||||
cookie?: string | boolean;
|
||||
|
||||
initialPacket?: any;
|
||||
/**
|
||||
* Whether to let engine.io handle the OPTIONS requests.
|
||||
* You can also pass a custom function to handle the requests
|
||||
* Configuration of the cookie that contains the client sid to send as part of handshake response headers. This cookie
|
||||
* might be used for sticky-session. Defaults to not sending any cookie.
|
||||
* @default false
|
||||
*/
|
||||
cookie?: any | boolean;
|
||||
/**
|
||||
* The options that will be forwarded to the cors module
|
||||
*/
|
||||
cors?: CorsOptions;
|
||||
/**
|
||||
* Whether to enable compatibility with Socket.IO v2 clients
|
||||
* @default false
|
||||
*/
|
||||
allowEIO3?: boolean;
|
||||
/**
|
||||
* Destroy unhandled upgrade requests
|
||||
* @default true
|
||||
*/
|
||||
handlePreflightRequest?: ((req: any, res: any) => void) | boolean;
|
||||
destroyUpgrade?: boolean;
|
||||
/**
|
||||
* Milliseconds after which unhandled requests are ended
|
||||
* @default 1000
|
||||
*/
|
||||
destroyUpgradeTimeout?: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user