Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ctx.controllerName and ctx.controllerMethodeName #1199

Merged
merged 4 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified backers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion docs/docs/architecture/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class AppController implements IAppController {

On every request, the controller method is called with a `Context` object. This context is unique and specific to the request.

It has five properties:
It has seven properties:

| Name | Type | Description |
| --- | --- | --- |
Expand All @@ -104,6 +104,8 @@ It has five properties:
| `user` | `{ [key: string]: any }`\|`null` | The current user (see [Authentication](../authentication/quick-start.md)). |
| `session`| `Session`\|`null` | The session object if you use sessions. |
| `files` | `FileList` | A list of file paths or buffers if you uploaded files (see [Upload and download files](../common/file-storage/upload-and-download-files.md)). |
| `controllerName` | `string` | The name of the controller class. |
| `controllerMethodName` | `string` | The name of the controller method. |

The types of the `user` and `state` properties are generic. You override their types if needed:

Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/core/http/context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ describe('Context', () => {
strictEqual(actual.user, null);
strictEqual(actual.session, null);
strictEqual(actual.files instanceof FileList, true);
strictEqual(actual.controllerName, '');
strictEqual(actual.controllerMethodName, '');

const actual2 = new Context(request, 'AppController', 'foobar');
strictEqual(actual2.controllerName, 'AppController');
strictEqual(actual2.controllerMethodName, 'foobar');
});

});
11 changes: 10 additions & 1 deletion packages/core/src/core/http/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ interface Request extends IncomingMessage {
* - the user object if available,
* - the session object if available,
* - a file list object,
* - the name of the controller and the name of the method,
* - and a `state` object that can be used to pass data across several hooks.
*
* @export
Expand All @@ -118,17 +119,25 @@ export class Context<User = { [key: string]: any } | null, ContextState = { [key
readonly state: ContextState;
readonly files: FileList;

readonly controllerName: string;
readonly controllerMethodName: string;

/**
* Creates an instance of Context.
* @param {*} request - Either the express request object or a mock (for testing).
* @param {string} [controllerName=''] - The name of the controller.
* @param {string} [controllerMethodName=''] - The name of the method.
* @memberof Context
*/
constructor(request: any) {
constructor(request: any, controllerName: string = '', controllerMethodName: string = '') {
this.request = request;
this.session = null;

this.user = null as any;
this.state = {} as any;
this.files = new FileList();

this.controllerName = controllerName;
this.controllerMethodName = controllerMethodName;
}
}
24 changes: 15 additions & 9 deletions packages/core/src/express/create-app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,44 +195,47 @@ describe('createApp', () => {
it(
'should respond on DELETE, GET, PATCH, POST, PUT, HEAD, OPTIONS and "ALL" requests if a handler exists.',
async () => {
let actualGetContext: Context|undefined;

class MyController {
@Head('/foo')
head() {
headSomething() {
// A HEAD response does not have a body.
return new HttpResponseOK()
.setHeader('foo', 'bar');
}
@Get('/foo')
get() {
getSomething(ctx: Context) {
actualGetContext = ctx;
return new HttpResponseOK('get');
}
@Post('/foo')
post() {
postSomething() {
return new HttpResponseOK('post');
}
@Patch('/foo')
patch() {
patchSomething() {
return new HttpResponseOK('patch');
}
@Put('/foo')
put() {
putSomething() {
return new HttpResponseOK('put');
}
@Delete('/foo')
delete() {
deleteSomething() {
return new HttpResponseOK('delete');
}
@Options('/foo')
options() {
optionsSomething() {
return new HttpResponseOK('options');
}
@All('/bar')
all() {
allSomething() {
return new HttpResponseOK('all');
}
}
const app = await createApp(MyController);
return Promise.all([
await Promise.all([
request(app).get('/foo').expect('get'),
request(app).post('/foo').expect('post'),
request(app).patch('/foo').expect('patch'),
Expand All @@ -244,6 +247,9 @@ describe('createApp', () => {
request(app).get('/bar').expect('all'),
request(app).post('/bar').expect('all'),
]);

strictEqual(actualGetContext?.controllerName, 'MyController');
strictEqual(actualGetContext?.controllerMethodName, 'getSomething');
}
);

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/express/create-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export async function createApp(
for (const { route } of routes) {
app[route.httpMethod.toLowerCase()](route.path, async (req: any, res: any, next: (err?: any) => any) => {
try {
const ctx = new Context(req);
const ctx = new Context(req, route.controller.constructor.name, route.propertyKey);
// TODO: better test this line.
const response = await getResponse(route, ctx, services, appController);
sendResponse(response, res);
Expand Down
6 changes: 6 additions & 0 deletions packages/socket.io/src/architecture/websocket-context.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ describe('WebsocketContext', () => {
deepStrictEqual(actual.state, {});
strictEqual(actual.user, null);
strictEqual(actual.session, null);
strictEqual(actual.controllerName, '');
strictEqual(actual.controllerMethodName, '');

const actual2 = new WebsocketContext(eventName, payload, socket, 'AppController', 'foobar');
strictEqual(actual2.controllerName, 'AppController');
strictEqual(actual2.controllerMethodName, 'foobar');
});

});
9 changes: 8 additions & 1 deletion packages/socket.io/src/architecture/websocket-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,26 @@ export class WebsocketContext<User = { [key: string]: any } | null, ContextState
user: User;
readonly state: ContextState;

readonly controllerName: string;
readonly controllerMethodName: string;

/**
* Creates an instance of WebsocketContext.
* @param {string} eventName The event name.
* @param {*} payload The request payload.
* @param {*} [socket={}] Either the socket object or a mock (for testing).
* @param {string} [controllerName=''] The name of the controller.
* @param {string} [controllerMethodName=''] The name of the method.
* @memberof WebsocketContext
*/
constructor(public eventName: string, public payload: any, socket: any = {}) {
constructor(public eventName: string, public payload: any, socket: any = {}, controllerName: string = '', controllerMethodName: string = '') {
this.socket = socket;
this.session = null;

this.user = null as any;
this.state = {} as any;

this.controllerName = controllerName;
this.controllerMethodName = controllerMethodName;
}
}
3 changes: 3 additions & 0 deletions packages/socket.io/src/socketio-controller.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ describe('SocketIOController', () => {
notDeepStrictEqual(actualContext?.socket, {});
deepStrictEqual(actualContext?.state, {});
strictEqual(actualContext?.user, null);

strictEqual(actualContext?.controllerName, 'WebsocketController');
strictEqual(actualContext?.controllerMethodName, 'createUser');
});

it('and should return an ok response if the controller method returns a WebsocketResponse (with no payload).', async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/socket.io/src/socketio-controller.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export abstract class SocketIOController implements ISocketIOController {
payload = undefined;
}

const ctx = new WebsocketContext(route.eventName, payload, socket);
const ctx = new WebsocketContext(route.eventName, payload, socket, route.controller.constructor.name, route.propertyKey);
const response = await getWebsocketResponse(route, ctx, this.services, this);

if (typeof cb !== 'function') {
Expand Down