Skip to content

Commit

Permalink
merge: release v0.1.2 (#551)
Browse files Browse the repository at this point in the history
  • Loading branch information
attilaorosz authored Jan 30, 2023
2 parents 795ef99 + 4c2553d commit 80474e7
Show file tree
Hide file tree
Showing 16 changed files with 347 additions and 55 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

_This changelog follows the [keep a changelog][keep-a-changelog]_ format to maintain a human readable changelog.

## [0.1.2](https://github.com/typestack/socket-controllers/compare/v0.1.1...v0.1.2) (2023-01-30)

### Added

- Added scoped controller support

```typescript
// create and run socket server
const server = new SocketControllers({
...
scopedContainerGetter: (args: ScopedContainerGetterParams) => {
// Return a container instance to be used to instantiate
// the controllers and their dependencies on each event
}
});
```


## [0.1.1](https://github.com/typestack/socket-controllers/compare/v0.1.0...v0.1.1) (2023-01-27)

### Added
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,35 @@ export class MessageController {

> Note: TypeDI won't create instances for unknown classes since 0.9.0, you have to decorate your Class as a `Service()` as well.
### Scoped controllers

You can enable scoped controllers by providing a `scopedContainerGetter` function in SocketServerOptions. This function should return a new container that will be used to instantiate the controller and its dependencies.

You will get a new instance for each event in the controller.

The `scopedContainerGetter` function receives a parameter which contains the socket, socket.io instance, event type, event name, namespace parameters and the message arguments if they are applicable.

```typescript
import 'reflect-metadata';
import { SocketControllers, ScopedContainerGetterParams } from 'socket-controllers';
import { Container, Token } from "typedi";

const myDiToken = new Token();

// create and run socket server
const server = new SocketControllers({
port: 3000,
container: Container,
scopedContainerGetter: (args: ScopedContainerGetterParams) => {
const container = Container.of(YOUR_REQUEST_CONTEXT);
container.set(myDiToken, 'MY_VALUE');
return container;
},
controllers: [__dirname + '/controllers/*.js'],
middlewares: [__dirname + '/middlewares/*.js'],
});
```

## Decorators Reference

| Signature | Description |
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "socket-controllers",
"version": "0.1.1",
"version": "0.1.2",
"description": "Use class-based controllers to handle websocket events.",
"license": "MIT",
"main": "index.js",
Expand Down Expand Up @@ -44,7 +44,7 @@
"@types/socket.io": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^5.49.0",
"@typescript-eslint/parser": "^5.49.0",
"eslint": "^8.32.0",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-jest": "^27.2.1",
"express": "^4.18.2",
Expand Down
79 changes: 49 additions & 30 deletions src/SocketControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { HandlerType } from './types/enums/HandlerType';
import { SocketControllersOptions } from './types/SocketControllersOptions';
import { ControllerMetadata } from './types/ControllerMetadata';
import { MiddlewareMetadata } from './types/MiddlewareMetadata';
import { ActionType } from './types/enums/ActionType';
import { SocketEventType } from './types/enums/SocketEventType';
import { ActionMetadata } from './types/ActionMetadata';
import { ParameterMetadata } from './types/ParameterMetadata';
import { ParameterType } from './types/enums/ParameterType';
Expand All @@ -18,12 +18,13 @@ import { TransformOptions } from './types/TransformOptions';
import { defaultTransformOptions } from './types/constants/defaultTransformOptions';
import { ActionTransformOptions } from './types/ActionTransformOptions';
import { instanceToPlain, plainToInstance } from 'class-transformer';
import { ScopedContainerGetterParams } from './types/ScopedContainerGetterParams';
import { MiddlewareInterface } from './types/MiddlewareInterface';

export class SocketControllers {
public container: { get<T>(someClass: { new (...args: any[]): T } | Function): T };
public controllers: HandlerMetadata<any, ControllerMetadata>[];
public middlewares: HandlerMetadata<MiddlewareInterface, MiddlewareMetadata>[];
public controllers: HandlerMetadata<ControllerMetadata>[];
public middlewares: HandlerMetadata<MiddlewareMetadata>[];
public io: Server;
public transformOptions: TransformOptions;

Expand All @@ -34,23 +35,14 @@ export class SocketControllers {
...defaultTransformOptions,
...options.transformOption,
};
this.controllers = this.loadHandlers<Function, ControllerMetadata>(
options.controllers || [],
HandlerType.CONTROLLER
);
this.middlewares = this.loadHandlers<MiddlewareInterface, MiddlewareMetadata>(
options.middlewares || [],
HandlerType.MIDDLEWARE
);
this.controllers = this.loadHandlers<ControllerMetadata>(options.controllers || [], HandlerType.CONTROLLER);
this.middlewares = this.loadHandlers<MiddlewareMetadata>(options.middlewares || [], HandlerType.MIDDLEWARE);

this.registerMiddlewares();
this.registerControllers();
}

private loadHandlers<T extends Object, U>(
handlers: Array<Function | string>,
type: HandlerType
): HandlerMetadata<T, U>[] {
private loadHandlers<T extends Object>(handlers: Array<Function | string>, type: HandlerType): HandlerMetadata<T>[] {
const loadedHandlers: Function[] = [];

for (const handler of handlers) {
Expand All @@ -64,7 +56,7 @@ export class SocketControllers {
return loadedHandlers.map(handler => {
return {
metadata: getMetadata(handler),
instance: this.container.get(handler),
target: handler,
};
});
}
Expand Down Expand Up @@ -101,7 +93,7 @@ export class SocketControllers {
const middlewaresWithNamespace = middlewares.filter(middleware => !!middleware.metadata.namespace);

for (const middleware of middlewaresWithoutNamespace) {
this.registerMiddleware(this.io as unknown as Namespace, middleware.instance);
this.registerMiddleware(this.io as unknown as Namespace, middleware);
}

this.io.on('new_namespace', (namespace: Namespace) => {
Expand All @@ -116,7 +108,7 @@ export class SocketControllers {
});

if (shouldApply) {
this.registerMiddleware(namespace, middleware.instance);
this.registerMiddleware(namespace, middleware);
}
}
});
Expand All @@ -132,7 +124,7 @@ export class SocketControllers {
}
});

const controllerNamespaceMap: Record<string, HandlerMetadata<unknown, ControllerMetadata>[]> = {};
const controllerNamespaceMap: Record<string, HandlerMetadata<ControllerMetadata>[]> = {};
const controllerNamespaceRegExpMap: Record<string, string | RegExp> = {};

for (const controller of controllersWithNamespace) {
Expand All @@ -156,18 +148,18 @@ export class SocketControllers {
}
}

private registerController(socket: Socket, controller: HandlerMetadata<any, ControllerMetadata>) {
private registerController(socket: Socket, controller: HandlerMetadata<ControllerMetadata>) {
const connectedAction = Object.values(controller.metadata.actions || {}).find(
action => action.type === ActionType.CONNECT
action => action.type === SocketEventType.CONNECT
);
const disconnectedAction = Object.values(controller.metadata.actions || {}).find(
action => action.type === ActionType.DISCONNECT
action => action.type === SocketEventType.DISCONNECT
);
const disconnectingAction = Object.values(controller.metadata.actions || {}).find(
action => action.type === ActionType.DISCONNECTING
action => action.type === SocketEventType.DISCONNECTING
);
const messageActions = Object.values(controller.metadata.actions || {}).filter(
action => action.type === ActionType.MESSAGE
action => action.type === SocketEventType.MESSAGE
);

if (connectedAction) {
Expand Down Expand Up @@ -195,20 +187,29 @@ export class SocketControllers {
messages.push(ack);
}

this.executeAction(socket, controller, messageAction, messages);
this.executeAction(socket, controller, messageAction, messageAction.options.name as string, messages);
});
}
}

private executeAction(
socket: Socket,
controller: HandlerMetadata<any, ControllerMetadata>,
controller: HandlerMetadata<ControllerMetadata>,
action: ActionMetadata,
eventName?: string,
data?: any[]
) {
const parameters = this.resolveParameters(socket, controller.metadata, action.parameters || [], data);
try {
const actionResult = controller.instance[action.methodName](...parameters);
let container = this.container;
if (this.options.scopedContainerGetter) {
container = this.options.scopedContainerGetter(
this.collectScopedContainerParams(socket, action.type, eventName, data, controller.metadata.namespace)
);
}

const controllerInstance: any = container.get(controller.target);
const actionResult = controllerInstance[action.methodName](...parameters);
Promise.resolve(actionResult)
.then(result => {
this.handleActionResult(socket, action, result, ResultType.EMIT_ON_SUCCESS);
Expand Down Expand Up @@ -251,9 +252,10 @@ export class SocketControllers {
}
}

private registerMiddleware(namespace: Namespace, middleware: MiddlewareInterface) {
private registerMiddleware(namespace: Namespace, middleware: HandlerMetadata<MiddlewareMetadata>) {
namespace.use((socket: Socket, next: (err?: any) => void) => {
middleware.use(socket, next);
const instance: MiddlewareInterface = this.container.get(middleware.target);
instance.use(socket, next);
});
}

Expand Down Expand Up @@ -334,10 +336,27 @@ export class SocketControllers {
return value;
}

private collectScopedContainerParams(
socket: Socket,
eventType: SocketEventType,
eventName?: string,
messageBody?: any[],
namespace?: string | RegExp
): ScopedContainerGetterParams {
return {
eventType,
eventName,
socket,
socketIo: this.io,
nspParams: this.extractNamespaceParameters(socket, namespace),
messageArgs: messageBody,
};
}

private extractNamespaceParameters(
socket: Socket,
namespace: string | RegExp | undefined,
parameterMetadata: ParameterMetadata
parameterMetadata?: ParameterMetadata
) {
const keys: any[] = [];
const regexp = namespace instanceof RegExp ? namespace : pathToRegexp(namespace || '/', keys);
Expand Down
4 changes: 2 additions & 2 deletions src/decorators/OnConnect.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';
import { ActionType } from '../types/enums/ActionType';
import { SocketEventType } from '../types/enums/SocketEventType';

export function OnConnect(): Function {
return function (object: Object, methodName: string) {
addActionToControllerMetadata(object.constructor, {
methodName,
type: ActionType.CONNECT,
type: SocketEventType.CONNECT,
options: {},
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/decorators/OnDisconnect.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';
import { ActionType } from '../types/enums/ActionType';
import { SocketEventType } from '../types/enums/SocketEventType';

export function OnDisconnect(): Function {
return function (object: Object, methodName: string) {
addActionToControllerMetadata(object.constructor, {
methodName,
type: ActionType.DISCONNECT,
type: SocketEventType.DISCONNECT,
options: {},
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/decorators/OnDisconnecting.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';
import { ActionType } from '../types/enums/ActionType';
import { SocketEventType } from '../types/enums/SocketEventType';

export function OnDisconnecting(): Function {
return function (object: Object, methodName: string) {
addActionToControllerMetadata(object.constructor, {
methodName,
type: ActionType.DISCONNECTING,
type: SocketEventType.DISCONNECTING,
options: {},
});
};
Expand Down
4 changes: 2 additions & 2 deletions src/decorators/OnMessage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { addActionToControllerMetadata } from '../util/add-action-to-controller-metadata';
import { ActionType } from '../types/enums/ActionType';
import { SocketEventType } from '../types/enums/SocketEventType';

export function OnMessage(name?: string): Function {
return function (object: Object, methodName: string) {
addActionToControllerMetadata(object.constructor, {
methodName,
type: ActionType.MESSAGE,
type: SocketEventType.MESSAGE,
options: { name },
});
};
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ export * from './decorators/SocketRooms';
export * from './types/MiddlewareInterface';
export * from './types/TransformOptions';
export * from './types/SocketControllersOptions';
export * from './types/enums/SocketEventType';

export * from './SocketControllers';
4 changes: 2 additions & 2 deletions src/types/ActionMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ParameterMetadata } from './ParameterMetadata';
import { ResultMetadata } from './ResultMetadata';
import { ActionType } from './enums/ActionType';
import { SocketEventType } from './enums/SocketEventType';

export interface ActionMetadata {
type: ActionType;
type: SocketEventType;
methodName: string;
options: any;
parameters: ParameterMetadata[];
Expand Down
6 changes: 3 additions & 3 deletions src/types/HandlerMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface HandlerMetadata<T, U> {
instance: T;
metadata: U;
export interface HandlerMetadata<T> {
metadata: T;
target: Function;
}
Loading

0 comments on commit 80474e7

Please sign in to comment.