-
-
Notifications
You must be signed in to change notification settings - Fork 7.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(common): Make ValidationPipe aware of WebSocket context
Extended logic of UsesPipes and ValidationPipe decorators to give awareness of WebSocket context. Closes #13190
- Loading branch information
1 parent
f724d84
commit 7ca2f06
Showing
13 changed files
with
239 additions
and
32 deletions.
There are no files selected for viewing
103 changes: 103 additions & 0 deletions
103
integration/websockets/e2e/gateway-validation-pipe.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { INestApplication } from '@nestjs/common'; | ||
import { WsAdapter } from '@nestjs/platform-ws'; | ||
import { Test } from '@nestjs/testing'; | ||
import * as WebSocket from 'ws'; | ||
import { ValidationPipeGateway } from '../src/validation-pipe.gateway'; | ||
import { expect } from 'chai'; | ||
import { ApplicationGateway } from '../src/app.gateway'; | ||
|
||
async function createNestApp(...gateways): Promise<INestApplication> { | ||
const testingModule = await Test.createTestingModule({ | ||
providers: gateways, | ||
}).compile(); | ||
const app = testingModule.createNestApplication(); | ||
app.useWebSocketAdapter(new WsAdapter(app) as any); | ||
return app; | ||
} | ||
|
||
const testBody = { ws: null, app: null }; | ||
|
||
async function prepareGatewayAndClientForResponseAction( | ||
gateway: typeof ValidationPipeGateway | ApplicationGateway, | ||
action: () => void, | ||
) { | ||
testBody.app = await createNestApp(gateway); | ||
await testBody.app.listen(3000); | ||
|
||
testBody.ws = new WebSocket('ws://localhost:8080'); | ||
await new Promise(resolve => testBody.ws.on('open', resolve)); | ||
|
||
testBody.ws.send( | ||
JSON.stringify({ | ||
event: 'push', | ||
data: { | ||
stringProp: 123, | ||
}, | ||
}), | ||
); | ||
|
||
action(); | ||
} | ||
|
||
const UNCAUGHT_EXCEPTION = 'uncaughtException'; | ||
|
||
type WsExceptionWithWrappedValidationError = { | ||
getError: () => { | ||
response: { | ||
message: string[]; | ||
}; | ||
}; | ||
}; | ||
|
||
function prepareToHandleExpectedUncaughtException() { | ||
const listeners = process.listeners(UNCAUGHT_EXCEPTION); | ||
process.removeAllListeners(UNCAUGHT_EXCEPTION); | ||
|
||
process.on( | ||
UNCAUGHT_EXCEPTION, | ||
(err: WsExceptionWithWrappedValidationError) => { | ||
expect(err.getError().response.message[0]).to.equal( | ||
'stringProp must be a string', | ||
); | ||
reattachUncaughtExceptionListeners(listeners); | ||
}, | ||
); | ||
} | ||
|
||
function reattachUncaughtExceptionListeners( | ||
listeners: NodeJS.UncaughtExceptionListener[], | ||
) { | ||
process.removeAllListeners(UNCAUGHT_EXCEPTION); | ||
for (const listener of listeners) { | ||
process.on(UNCAUGHT_EXCEPTION, listener); | ||
} | ||
} | ||
|
||
describe('WebSocketGateway with ValidationPipe', () => { | ||
it(`should throw WsException`, async () => { | ||
prepareToHandleExpectedUncaughtException(); | ||
|
||
await prepareGatewayAndClientForResponseAction( | ||
ValidationPipeGateway, | ||
() => { | ||
testBody.ws.once('message', () => {}); | ||
}, | ||
); | ||
}); | ||
|
||
it('should return message normally', async () => { | ||
await new Promise<void>(resolve => | ||
prepareGatewayAndClientForResponseAction(ApplicationGateway, async () => { | ||
testBody.ws.once('message', msg => { | ||
expect(JSON.parse(msg).data.stringProp).to.equal(123); | ||
resolve(); | ||
}); | ||
}), | ||
); | ||
}); | ||
|
||
afterEach(function (done) { | ||
testBody.ws.close(); | ||
testBody.app.close().then(() => done()); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { | ||
ArgumentsHost, | ||
Catch, | ||
UseFilters, | ||
UsePipes, | ||
ValidationPipe, | ||
} from '@nestjs/common'; | ||
import { | ||
BaseWsExceptionFilter, | ||
MessageBody, | ||
SubscribeMessage, | ||
WebSocketGateway, | ||
} from '@nestjs/websockets'; | ||
import { IsString } from 'class-validator'; | ||
|
||
class TestModel { | ||
@IsString() | ||
stringProp: string; | ||
} | ||
|
||
@Catch() | ||
export class AllExceptionsFilter extends BaseWsExceptionFilter { | ||
catch(exception: unknown, host: ArgumentsHost) { | ||
throw exception; | ||
} | ||
} | ||
|
||
@WebSocketGateway(8080) | ||
@UsePipes(new ValidationPipe()) | ||
@UseFilters(new AllExceptionsFilter()) | ||
export class ValidationPipeGateway { | ||
@SubscribeMessage('push') | ||
onPush(@MessageBody() data: TestModel) { | ||
console.log('received msg'); | ||
return { | ||
event: 'push', | ||
data, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { isObject, isString } from '../utils/shared.utils'; | ||
|
||
export class WsException extends Error { | ||
constructor(private readonly error: string | object) { | ||
super(); | ||
this.initMessage(); | ||
} | ||
|
||
public initMessage() { | ||
if (isString(this.error)) { | ||
this.message = this.error; | ||
} else if ( | ||
isObject(this.error) && | ||
isString((this.error as Record<string, any>).message) | ||
) { | ||
this.message = (this.error as Record<string, any>).message; | ||
} else if (this.constructor) { | ||
this.message = this.constructor.name | ||
.match(/[A-Z][a-z]+|[0-9]+/g) | ||
.join(' '); | ||
} | ||
} | ||
|
||
public getError(): string | object { | ||
return this.error; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
packages/common/interfaces/features/target-aware-pipe.interface.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* Interface describing method to set the target of the pipe decorator | ||
*/ | ||
export interface TargetAwarePipe { | ||
isTargetAware: true; | ||
|
||
setTarget(target: unknown): void; | ||
} | ||
|
||
export function isTargetAware(pipe: unknown): pipe is TargetAwarePipe { | ||
return pipe['isTargetAware']; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
export * from './ws-exception'; | ||
export { WsException } from '@nestjs/common/exceptions/ws-exception'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,8 @@ | ||
import { isObject, isString } from '@nestjs/common/utils/shared.utils'; | ||
import { WsException as WSE } from '@nestjs/common/exceptions/ws-exception'; | ||
|
||
export class WsException extends Error { | ||
constructor(private readonly error: string | object) { | ||
super(); | ||
this.initMessage(); | ||
} | ||
/** | ||
* @deprecated WsException has been moved to @nestjs/common | ||
*/ | ||
const WsException = WSE; | ||
|
||
public initMessage() { | ||
if (isString(this.error)) { | ||
this.message = this.error; | ||
} else if ( | ||
isObject(this.error) && | ||
isString((this.error as Record<string, any>).message) | ||
) { | ||
this.message = (this.error as Record<string, any>).message; | ||
} else if (this.constructor) { | ||
this.message = this.constructor.name | ||
.match(/[A-Z][a-z]+|[0-9]+/g) | ||
.join(' '); | ||
} | ||
} | ||
|
||
public getError(): string | object { | ||
return this.error; | ||
} | ||
} | ||
export { WsException }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters