Skip to content

Commit

Permalink
feat(http): also read path parameters in HttpRequestParser<T>
Browse files Browse the repository at this point in the history
This allows to access in http listeners all parameters that come from the url.

```typescript
@http.GET('team/:id')
async team(id: string) {}

onAuth.listen((event, parser: HttpRequestParser<{id: string}>) => {
  const data = await parser();
  console.log(data.teamId);
});
```

These parameters must be present in the method/function definition, otherwise they are not included (the definition alone in the path string is not enough)
  • Loading branch information
marcj committed Feb 6, 2024
1 parent ecc9c28 commit 888d058
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 4 deletions.
23 changes: 20 additions & 3 deletions packages/http/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,16 @@ export type HttpBody<T> = T & TypeAnnotation<'httpBody'>;
export type HttpBodyValidation<T> = ValidatedBody<T> & TypeAnnotation<'httpBodyValidation'>;

export interface HttpRequestParserOptions {
withPath?: boolean;
withBody?: boolean;
withQuery?: boolean;
withHeader?: boolean;
}

/**
* Delays the parsing of the body/query/header to the very last moment, when the parameter is actually used.
* Delays the parsing of the path/body/query/header to the very last moment, when the parameter is actually used.
*
* If no options are provided, the parser will receive data from header, body, and query, in this order.
* If no options are provided, the parser will receive data from path, header, body, and query, in this order.
* This basically allows to fetch data from all possible HTTP sources in one go.
*
* You can disable various sources by providing the options, e.g. `{withBody: false}` to disable body parsing.
Expand All @@ -158,7 +159,23 @@ export interface HttpRequestParserOptions {
* }
* ```
*
* This is necessary in event listeners, since they are instantiated synchronously,
* Note that the parsers is based on all defined parameters (e.g. `userId: HttpQuery<string>` => {userId: string}),
* and then starts from there applying header, body, and then query values.
* This means you also get access to defined path parameters, like:
*
* ```typescript
* @http.GET('teams/:teamId')
* async route(teamId: string) {
* //teamId is string
* }
*
* httpWorkflow.onController.listen((event, parser: HttpRequestParser<{teamId: string}>) => {
* const data = await parser();
* console.log(data.teamId);
* });
* ```
*
* HttpRequestParser is necessary in event listeners, since they are instantiated synchronously,
* but body is parsed asynchronously. So use in event listeners HttpRequestParser instead of HttpBody.
*/
export type HttpRequestParser<T> = ((options?: HttpRequestParserOptions) => Promise<T>) & TypeAnnotation<'httpRequestParser', T>;
Expand Down
2 changes: 1 addition & 1 deletion packages/http/src/request-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export function getRequestParserCodeForParameters(
const parseOptionsVar = compiler.reserveVariable('parseOptions', parseOptions);
const parseBodyVar = compiler.reserveVariable('parseBody', parseBody);
setParameters.push(`parameters.${parameter.parameter.name} = async (options = {}) => {
let res = {};
let res = options.withPath !== false ? Object.assign({}, parameters) : {};
if (options.withHeader !== false) {
Object.assign(res, _headers);
}
Expand Down
6 changes: 6 additions & 0 deletions packages/http/tests/router.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1316,6 +1316,11 @@ test('body and queries in listener', async () => {
const data = await parser();
return [data.auth, data.userId];
}
@http.GET('/5/:userId')
async handle5(userId: number, parser: HttpRequestParser<AuthData>) {
const data = await parser();
return [userId, data.auth, data.userId];
}
}

type AuthData = {
Expand Down Expand Up @@ -1345,6 +1350,7 @@ test('body and queries in listener', async () => {
expect((await httpKernel.request(HttpRequest.POST('/3?auth=secretToken1').json({userId: '24'}))).json).toEqual([24, 24, 'secretToken1']);
expect((await httpKernel.request(HttpRequest.GET('/4?auth=secretToken1&userId=1'))).json).toEqual(['secretToken1', '1']);
expect((await httpKernel.request(HttpRequest.GET('/4?userId=1').header('auth', 'secretToken1'))).json).toEqual(['secretToken1', '1']);
expect((await httpKernel.request(HttpRequest.GET('/5/1?auth=secretToken1'))).json).toEqual([1, 'secretToken1', '1']);
});

test('stream', async () => {
Expand Down

0 comments on commit 888d058

Please sign in to comment.