Skip to content

Commit f8532e1

Browse files
authored
Merge pull request #21 from rustamwin/dynamic-namespace
Added dynamic namespace support
2 parents f9b2530 + d145613 commit f8532e1

File tree

11 files changed

+168
-34
lines changed

11 files changed

+168
-34
lines changed

package-lock.json

Lines changed: 17 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
},
3232
"dependencies": {
3333
"class-transformer": "^0.1.6",
34+
"path-to-regexp": "^3.0.0",
3435
"reflect-metadata": "^0.1.10",
3536
"socket.io": "^2.0.1"
3637
},
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export class Message {
2+
3+
id: number;
4+
text: string;
5+
6+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
OnConnect,
3+
SocketController,
4+
ConnectedSocket,
5+
OnDisconnect,
6+
MessageBody,
7+
OnMessage,
8+
NspParams
9+
} from "../../src/decorators";
10+
import {Message} from "./Message";
11+
12+
@SocketController("/messages/:id")
13+
export class MessageController {
14+
15+
@OnConnect()
16+
connection(@ConnectedSocket() socket: any) {
17+
console.log("client connected");
18+
}
19+
20+
@OnDisconnect()
21+
disconnect(@ConnectedSocket() socket: any) {
22+
console.log("client disconnected");
23+
}
24+
25+
@OnMessage("save")
26+
async save(@ConnectedSocket() socket: any, @MessageBody() message: Message, @NspParams() params: any[]) {
27+
console.log("received message:", message);
28+
console.log("namespace params:", params);
29+
console.log("setting id to the message and sending it back to the client");
30+
message.id = 1;
31+
socket.emit("message_saved", message);
32+
}
33+
34+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import "reflect-metadata";
2+
import {createSocketServer} from "../../src/index";
3+
import "./MessageController";
4+
5+
createSocketServer(3001); // creates socket.io server and registers all controllers there
6+
7+
console.log("Socket.io is up and running on port 3001. Send messages via socket-io client.");
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<html>
2+
<script src="../../node_modules/socket.io-client/dist/socket.io.js"></script>
3+
<script>
4+
var socket = io("http://localhost:3001/messages/1");
5+
socket.on("message_saved", function (message) {
6+
console.log("Saved message received back: ", message);
7+
});
8+
9+
function onClick() {
10+
socket.emit("save", { text: "Hello this is message" });
11+
}
12+
</script>
13+
<body>
14+
15+
Watch console for events.<br/>
16+
<button onclick="onClick()">Click to send a save event to the /messages/1 namespace.</button>
17+
18+
</body>
19+
</html>

src/SocketControllerExecutor.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {ParamMetadata} from "./metadata/ParamMetadata";
66
import {ParameterParseJsonError} from "./error/ParameterParseJsonError";
77
import {ParamTypes} from "./metadata/types/ParamTypes";
88
import {ControllerMetadata} from "./metadata/ControllerMetadata";
9+
import * as pathToRegexp from "path-to-regexp";
910

1011
/**
1112
* Registers controllers and actions in the given server framework.
@@ -91,7 +92,11 @@ export class SocketControllerExecutor {
9192

9293
// register controllers with namespaces
9394
controllersWithNamespaces.forEach(controller => {
94-
this.io.of(controller.namespace).on("connection", (socket: any) => this.handleConnection([controller], socket));
95+
let namespace: string | RegExp = controller.namespace;
96+
if (!(namespace instanceof RegExp)) {
97+
namespace = pathToRegexp(namespace);
98+
}
99+
this.io.of(namespace).on("connection", (socket: any) => this.handleConnection([controller], socket));
95100
});
96101

97102
return this;
@@ -101,20 +106,20 @@ export class SocketControllerExecutor {
101106
controllers.forEach(controller => {
102107
controller.actions.forEach(action => {
103108
if (action.type === ActionTypes.CONNECT) {
104-
this.handleAction(action, { socket: socket })
109+
this.handleAction(action, {socket: socket})
105110
.then(result => this.handleSuccessResult(result, action, socket))
106111
.catch(error => this.handleFailResult(error, action, socket));
107112

108113
} else if (action.type === ActionTypes.DISCONNECT) {
109114
socket.on("disconnect", () => {
110-
this.handleAction(action, { socket: socket })
115+
this.handleAction(action, {socket: socket})
111116
.then(result => this.handleSuccessResult(result, action, socket))
112117
.catch(error => this.handleFailResult(error, action, socket));
113118
});
114119

115120
} else if (action.type === ActionTypes.MESSAGE) {
116121
socket.on(action.name, (data: any) => {
117-
this.handleAction(action, { socket: socket, data: data })
122+
this.handleAction(action, {socket: socket, data: data})
118123
.then(result => this.handleSuccessResult(result, action, socket))
119124
.catch(error => this.handleFailResult(error, action, socket));
120125
});
@@ -123,8 +128,8 @@ export class SocketControllerExecutor {
123128
});
124129
}
125130

126-
private handleAction(action: ActionMetadata, options: { socket?: any, data?: any }): Promise<any> {
127-
131+
private handleAction(action: ActionMetadata, options: {socket?: any, data?: any}): Promise<any> {
132+
128133
// compute all parameters
129134
const paramsPromises = action.params
130135
.sort((param1, param2) => param1.index - param2.index)
@@ -147,6 +152,13 @@ export class SocketControllerExecutor {
147152
} else if (param.type === ParamTypes.SOCKET_ROOMS) {
148153
return options.socket.rooms;
149154

155+
} else if (param.type === ParamTypes.NAMESPACE_PARAMS) {
156+
return this.handleNamespaceParams(options.socket, action, param);
157+
158+
} else if (param.type === ParamTypes.NAMESPACE_PARAM) {
159+
const params: any[] = this.handleNamespaceParams(options.socket, action, param);
160+
return params[param.value];
161+
150162
} else {
151163
return this.handleParam(param, options);
152164
}
@@ -162,7 +174,7 @@ export class SocketControllerExecutor {
162174
});
163175
}
164176

165-
private handleParam(param: ParamMetadata, options: { socket?: any, data?: any }) {
177+
private handleParam(param: ParamMetadata, options: {socket?: any, data?: any}) {
166178

167179
let value = options.data;
168180
if (value !== null && value !== undefined && value !== "")
@@ -241,4 +253,15 @@ export class SocketControllerExecutor {
241253
}
242254
}
243255

256+
private handleNamespaceParams(socket: any, action: ActionMetadata, param: ParamMetadata): any[] {
257+
const keys: any[] = [];
258+
const regexp = pathToRegexp(action.controllerMetadata.namespace, keys);
259+
const parts: any[] = regexp.exec(socket.nsp.name);
260+
const params: any[] = [];
261+
keys.forEach((key: any, index: number) => {
262+
params[key.name] = this.handleParamFormat(parts[index + 1], param);
263+
});
264+
return params;
265+
}
266+
244267
}

src/decorators.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {ResultTypes} from "./metadata/types/ResultTypes";
1414
*
1515
* @param namespace Namespace in which this controller's events will be registered.
1616
*/
17-
export function SocketController(namespace?: string) {
17+
export function SocketController(namespace?: string | RegExp) {
1818
return function (object: Function) {
1919
const metadata: SocketControllerMetadataArgs = {
2020
namespace: namespace,
@@ -104,7 +104,7 @@ export function SocketIO() {
104104
/**
105105
* Injects received message body.
106106
*/
107-
export function MessageBody(options?: { classTransformOptions?: ClassTransformOptions }) {
107+
export function MessageBody(options?: {classTransformOptions?: ClassTransformOptions}) {
108108
return function (object: Object, methodName: string, index: number) {
109109
let format = (Reflect as any).getMetadata("design:paramtypes", object, methodName)[index];
110110
const metadata: ParamMetadataArgs = {
@@ -171,6 +171,41 @@ export function SocketRequest() {
171171
};
172172
}
173173

174+
/**
175+
* Injects parameters of the connected socket namespace.
176+
*/
177+
export function NspParams() {
178+
return function (object: Object, methodName: string, index: number) {
179+
let format = (Reflect as any).getMetadata("design:paramtypes", object, methodName)[index];
180+
const metadata: ParamMetadataArgs = {
181+
target: object.constructor,
182+
method: methodName,
183+
index: index,
184+
type: ParamTypes.NAMESPACE_PARAMS,
185+
reflectedType: format
186+
};
187+
defaultMetadataArgsStorage().params.push(metadata);
188+
};
189+
}
190+
191+
/**
192+
* Injects named param from the connected socket namespace.
193+
*/
194+
export function NspParam(name: string) {
195+
return function (object: Object, methodName: string, index: number) {
196+
let format = (Reflect as any).getMetadata("design:paramtypes", object, methodName)[index];
197+
const metadata: ParamMetadataArgs = {
198+
target: object.constructor,
199+
method: methodName,
200+
index: index,
201+
type: ParamTypes.NAMESPACE_PARAM,
202+
reflectedType: format,
203+
value: name
204+
};
205+
defaultMetadataArgsStorage().params.push(metadata);
206+
};
207+
}
208+
174209
/**
175210
* Injects rooms of the connected socket client.
176211
*/
@@ -191,7 +226,7 @@ export function SocketRooms() {
191226
/**
192227
* Registers a new middleware to be registered in the socket.io.
193228
*/
194-
export function Middleware(options?: { priority?: number }): Function {
229+
export function Middleware(options?: {priority?: number}): Function {
195230
return function (object: Function) {
196231
const metadata: MiddlewareMetadataArgs = {
197232
target: object,
@@ -206,7 +241,7 @@ export function Middleware(options?: { priority?: number }): Function {
206241
* It will emit message only if controller succeed without errors.
207242
* If result is a Promise then it will wait until promise is resolved and emit a message.
208243
*/
209-
export function EmitOnSuccess(messageName: string, options?: { classTransformOptions?: ClassTransformOptions }): Function {
244+
export function EmitOnSuccess(messageName: string, options?: {classTransformOptions?: ClassTransformOptions}): Function {
210245
return function (object: Object, methodName: string) {
211246
const metadata: ResultMetadataArgs = {
212247
target: object.constructor,
@@ -224,7 +259,7 @@ export function EmitOnSuccess(messageName: string, options?: { classTransformOpt
224259
* It will emit message only if controller throw an exception.
225260
* If result is a Promise then it will wait until promise throw an error and emit a message.
226261
*/
227-
export function EmitOnFail(messageName: string, options?: { classTransformOptions?: ClassTransformOptions }): Function {
262+
export function EmitOnFail(messageName: string, options?: {classTransformOptions?: ClassTransformOptions}): Function {
228263
return function (object: Object, methodName: string) {
229264
const metadata: ResultMetadataArgs = {
230265
target: object.constructor,

src/metadata/ControllerMetadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ export class ControllerMetadata {
2121
/**
2222
* Base route for all actions registered in this controller.
2323
*/
24-
namespace: string;
24+
namespace: string | RegExp;
2525

2626
// -------------------------------------------------------------------------
2727
// Constructor
2828
// -------------------------------------------------------------------------
29-
29+
3030
constructor(args: SocketControllerMetadataArgs) {
3131
this.target = args.target;
3232
this.namespace = args.namespace;
@@ -39,5 +39,5 @@ export class ControllerMetadata {
3939
get instance(): any {
4040
return getFromContainer(this.target);
4141
}
42-
42+
4343
}

src/metadata/args/SocketControllerMetadataArgs.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ export interface SocketControllerMetadataArgs {
1111
/**
1212
* Extra namespace in which this controller's events will be registered.
1313
*/
14-
namespace?: string;
15-
14+
namespace?: string | RegExp;
15+
1616
}

0 commit comments

Comments
 (0)