Skip to content

Commit 1b864be

Browse files
committed
added decorators to handle namespace params
1 parent b443b8e commit 1b864be

File tree

9 files changed

+158
-24
lines changed

9 files changed

+158
-24
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: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
*/

src/metadata/types/ParamTypes.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ export type ParamType = "custom"
88
| "socket-io"
99
| "socket-id"
1010
| "socket-request"
11-
| "socket-rooms";
11+
| "socket-rooms"
12+
| "namespace-params"
13+
| "namespace-param";
1214

1315
/**
1416
* Controller action's parameter type.
@@ -18,8 +20,10 @@ export class ParamTypes {
1820
static CONNECTED_SOCKET: ParamType = "connected-socket";
1921
static SOCKET_BODY: ParamType = "socket-body";
2022
static SOCKET_QUERY_PARAM: ParamType = "socket-query-param";
21-
static SOCKET_IO = "socket-io";
22-
static SOCKET_ID = "socket-id";
23-
static SOCKET_REQUEST = "socket-request";
24-
static SOCKET_ROOMS = "socket-rooms";
23+
static SOCKET_IO: ParamType = "socket-io";
24+
static SOCKET_ID: ParamType = "socket-id";
25+
static SOCKET_REQUEST: ParamType = "socket-request";
26+
static SOCKET_ROOMS: ParamType = "socket-rooms";
27+
static NAMESPACE_PARAMS: ParamType = "namespace-params";
28+
static NAMESPACE_PARAM: ParamType = "namespace-param";
2529
}

0 commit comments

Comments
 (0)