fix(): fix middleware tests, fix socketio deprecations

This commit is contained in:
Kamil Myśliwiec
2021-01-29 15:24:09 +01:00
parent 12aa576a55
commit fc56e93c4b
14 changed files with 114 additions and 87 deletions

View File

@@ -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',
});

View File

@@ -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');

View File

@@ -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',
});

View File

@@ -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;
}
}
}

View File

@@ -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(

View File

@@ -91,7 +91,6 @@ export class MiddlewareModule {
const middlewareBuilder = new MiddlewareBuilder(
this.routesMapper,
this.httpAdapter,
this.container,
);
await instance.configure(middlewareBuilder);

View File

@@ -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(

View File

@@ -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;

View File

@@ -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);
});
});

View File

@@ -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,
},
]);
});

View File

@@ -75,7 +75,6 @@ describe('MiddlewareModule', () => {
new MiddlewareBuilder(
(middlewareModule as any).routesMapper,
undefined,
stubContainer,
),
),
).to.be.true;

View File

@@ -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);
}

View File

@@ -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 };
}

View File

@@ -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;
}