@travetto/web 6.0.2 → 7.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -22
- package/__index__.ts +2 -1
- package/package.json +11 -11
- package/src/common/global.ts +5 -4
- package/src/decorator/common.ts +26 -21
- package/src/decorator/controller.ts +8 -5
- package/src/decorator/endpoint.ts +21 -39
- package/src/decorator/param.ts +39 -32
- package/src/interceptor/body.ts +4 -4
- package/src/registry/registry-adapter.ts +185 -0
- package/src/registry/registry-index.ts +110 -0
- package/src/registry/types.ts +19 -56
- package/src/registry/visitor.ts +14 -11
- package/src/router/base.ts +14 -12
- package/src/types/headers.ts +1 -1
- package/src/util/common.ts +7 -7
- package/src/util/endpoint.ts +61 -26
- package/src/util/net.ts +14 -0
- package/support/test/dispatch-util.ts +5 -24
- package/support/test/suite/base.ts +7 -5
- package/support/test/suite/schema.ts +26 -20
- package/support/test/suite/standard.ts +2 -3
- package/src/registry/controller.ts +0 -292
- package/support/transformer.web.ts +0 -212
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/web/DOC.tsx and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# Web API
|
|
4
4
|
|
|
5
|
-
## Declarative support creating
|
|
5
|
+
## Declarative support for creating Web Applications
|
|
6
6
|
|
|
7
7
|
**Install: @travetto/web**
|
|
8
8
|
```bash
|
|
@@ -13,9 +13,9 @@ npm install @travetto/web
|
|
|
13
13
|
yarn add @travetto/web
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The module provides a declarative API for creating and describing a Web application. Since the framework is declarative, decorators are used to configure almost everything. The general layout of an application is a collection of [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#
|
|
16
|
+
The module provides a declarative API for creating and describing a Web application. Since the framework is declarative, decorators are used to configure almost everything. The general layout of an application is a collection of [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#L11)s that employ some combination of [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15)s to help manage which functionality is executed before the [Endpoint](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L14) code, within the [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#L11). This module will look at:
|
|
17
17
|
* Request/Response Pattern
|
|
18
|
-
* Defining a [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#
|
|
18
|
+
* Defining a [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#L11)
|
|
19
19
|
* Defining an [Endpoint](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L14)
|
|
20
20
|
* Using a [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15)
|
|
21
21
|
* Creating a Custom [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15)
|
|
@@ -91,7 +91,7 @@ export class WebResponse<B = unknown> extends BaseWebMessage<B, WebResponseConte
|
|
|
91
91
|
These objects do not represent the underlying sockets provided by various http servers, but in fact are simple wrappers that track the flow through the call stack of the various [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15)s and the [Endpoint](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L14) handler. One of the biggest departures here, is that the response is not an entity that is passed around from call-site to call-site, but is is solely a return-value. This doesn't mean the return value has to be static and pre-allocated, on the contrary streams are still supported. The difference here is that the streams/asynchronous values will be consumed until the response is sent back to the user. The [CompressInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/interceptor/compress.ts#L44) is a good reference for transforming a [WebResponse](https://github.com/travetto/travetto/tree/main/module/web/src/types/response.ts#L3) that can either be a stream or a fixed value.
|
|
92
92
|
|
|
93
93
|
## Defining a Controller
|
|
94
|
-
To start, we must define a [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#
|
|
94
|
+
To start, we must define a [@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#L11), which is only allowed on classes. Controllers can be configured with:
|
|
95
95
|
* `path` - The required context path the controller will operate atop
|
|
96
96
|
* `title` - The definition of the controller
|
|
97
97
|
* `description` - High level description fo the controller
|
|
@@ -114,13 +114,13 @@ class SimpleController {
|
|
|
114
114
|
Once the controller is declared, each method of the controller is a candidate for being an endpoint. By design, everything is asynchronous, and so async/await is natively supported.
|
|
115
115
|
|
|
116
116
|
The most common pattern is to register HTTP-driven endpoints. The HTTP methods that are currently supported:
|
|
117
|
-
* [@Get](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
118
|
-
* [@Post](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
119
|
-
* [@Put](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
117
|
+
* [@Get](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L41)
|
|
118
|
+
* [@Post](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L48)
|
|
119
|
+
* [@Put](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L55)
|
|
120
120
|
* [@Delete](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L69)
|
|
121
|
-
* [@Patch](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
122
|
-
* [@Head](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
123
|
-
* [@Options](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#
|
|
121
|
+
* [@Patch](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L62)
|
|
122
|
+
* [@Head](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L76)
|
|
123
|
+
* [@Options](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L83)
|
|
124
124
|
|
|
125
125
|
Similar to the Controller, each endpoint decorator handles the following config:
|
|
126
126
|
* `title` - The definition of the endpoint
|
|
@@ -155,12 +155,12 @@ class SimpleController {
|
|
|
155
155
|
|
|
156
156
|
### Parameters
|
|
157
157
|
Endpoints can be configured to describe and enforce parameter behavior. Request parameters can be defined in five areas:
|
|
158
|
-
* [@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
159
|
-
* [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
160
|
-
* [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
161
|
-
* [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
158
|
+
* [@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L38) - Path params
|
|
159
|
+
* [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L45) - Query params - can be either a single value or bind to a whole object
|
|
160
|
+
* [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L59) - Request body
|
|
161
|
+
* [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L52) - Header values
|
|
162
162
|
|
|
163
|
-
Each [@Param](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
163
|
+
Each [@Param](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L16) can be configured to indicate:
|
|
164
164
|
* `name` - Name of param, field name, defaults to handler parameter name if necessary
|
|
165
165
|
* `description` - Description of param, pulled from [JSDoc](http://usejsdoc.org/about-getting-started.html), or defaults to name if empty
|
|
166
166
|
* `required?` - Is the field required?, defaults to whether or not the parameter itself is optional
|
|
@@ -223,7 +223,7 @@ export class Simple {
|
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
### ContextParam
|
|
226
|
-
In addition to endpoint parameters (i.e. user-provided inputs), there may also be a desire to access indirect contextual information. Specifically you may need access to the entire [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11). These are able to be injected using the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
226
|
+
In addition to endpoint parameters (i.e. user-provided inputs), there may also be a desire to access indirect contextual information. Specifically you may need access to the entire [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11). These are able to be injected using the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) on a class-level field from the [WebAsyncContext](https://github.com/travetto/travetto/tree/main/module/web/src/context.ts#L11). These are not exposed as endpoint parameters as they cannot be provided when making RPC invocations.
|
|
227
227
|
|
|
228
228
|
**Code: Example ContextParam usage**
|
|
229
229
|
```typescript
|
|
@@ -251,12 +251,12 @@ class ContextController {
|
|
|
251
251
|
}
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
-
**Note**: When referencing the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
254
|
+
**Note**: When referencing the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) values, the contract for idempotency needs to be carefully inspected, if expected. You can see in the example above that the [@CacheControl](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/common.ts#L45) decorator is used to ensure that the response is not cached.
|
|
255
255
|
|
|
256
256
|
### Validating Inputs
|
|
257
257
|
The module provides high level access for [Schema](https://github.com/travetto/travetto/tree/main/module/schema#readme "Data type registry for runtime validation, reflection and binding.") support, via decorators, for validating and typing request inputs.
|
|
258
258
|
|
|
259
|
-
By default, all endpoint parameters are validated for type, and any additional constraints added (required, vs optional, minlength, etc). Each parameter location ([@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
259
|
+
By default, all endpoint parameters are validated for type, and any additional constraints added (required, vs optional, minlength, etc). Each parameter location ([@PathParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L38), [@Body](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L59), [@QueryParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L45), [@HeaderParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L52)) primarily provides a source to bind the endpoint arguments from. Once bound, the module will validate that the provided arguments are in fact valid. All validation will occur before the endpoint is ever executed, ensuring a strong contract.
|
|
260
260
|
|
|
261
261
|
**Code: Using Body for POST requests**
|
|
262
262
|
```typescript
|
|
@@ -617,7 +617,7 @@ export class CorsConfig {
|
|
|
617
617
|
```
|
|
618
618
|
|
|
619
619
|
#### CacheControlInterceptor
|
|
620
|
-
[CacheControlInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/interceptor/cache-control.ts#L23) by default, enforces whatever caching policy is established on a given endpoint using the [CacheControl](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/common.ts#
|
|
620
|
+
[CacheControlInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/interceptor/cache-control.ts#L23) by default, enforces whatever caching policy is established on a given endpoint using the [@CacheControl](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/common.ts#L45) decorator. Additionally, the interceptor retains knowledge if it is running on a private endpoint, or not. If the endpoint is deemed private it affects the caching header accordingly. If the endpoint directly returns a `Cache-Control` header, that takes precedence and all other logic is ignored.
|
|
621
621
|
|
|
622
622
|
This can be managed by setting `web.cache.applies: <boolean>` in your config.
|
|
623
623
|
|
|
@@ -675,11 +675,11 @@ export class AlowDenyController {
|
|
|
675
675
|
```
|
|
676
676
|
|
|
677
677
|
The resolution logic is as follows:
|
|
678
|
-
* Check the resolved [Endpoint](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L14)/[@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#
|
|
678
|
+
* Check the resolved [Endpoint](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/endpoint.ts#L14)/[@Controller](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/controller.ts#L11) overrides to see if an interceptor is explicitly allowed or disallowed
|
|
679
679
|
* Default to `applies()` logic for all available interceptors
|
|
680
680
|
|
|
681
681
|
## Creating a Custom WebInterceptor
|
|
682
|
-
Additionally it may be desirable to create a custom interceptor. Interceptors can be registered with the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") by implementing the [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15) interface and adding an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#
|
|
682
|
+
Additionally it may be desirable to create a custom interceptor. Interceptors can be registered with the [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support.") by implementing the [WebInterceptor](https://github.com/travetto/travetto/tree/main/module/web/src/types/interceptor.ts#L15) interface and adding an [@Injectable](https://github.com/travetto/travetto/tree/main/module/di/src/decorator.ts#L15) decorator. A simple logging interceptor:
|
|
683
683
|
|
|
684
684
|
**Code: Defining a new Interceptor**
|
|
685
685
|
```typescript
|
|
@@ -735,7 +735,7 @@ export class SimpleAuthInterceptor implements WebInterceptor {
|
|
|
735
735
|
```
|
|
736
736
|
|
|
737
737
|
## Cookie Support
|
|
738
|
-
Cookies are a unique element, within the framework, as they sit on the request and response flows. Ideally we would separate these out, but given the support for key rotation, there is a scenario in which reading a cookie on the request, will result in a cookie needing to be written on the response. Because of this, cookies are treated as being outside the normal [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11) activity, and is exposed as the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#
|
|
738
|
+
Cookies are a unique element, within the framework, as they sit on the request and response flows. Ideally we would separate these out, but given the support for key rotation, there is a scenario in which reading a cookie on the request, will result in a cookie needing to be written on the response. Because of this, cookies are treated as being outside the normal [WebRequest](https://github.com/travetto/travetto/tree/main/module/web/src/types/request.ts#L11) activity, and is exposed as the [@ContextParam](https://github.com/travetto/travetto/tree/main/module/web/src/decorator/param.ts#L66) [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12). The [CookieJar](https://github.com/travetto/travetto/tree/main/module/web/src/util/cookie.ts#L12) has a fairly basic contract:
|
|
739
739
|
|
|
740
740
|
**Code: CookieJar contract**
|
|
741
741
|
```typescript
|
package/__index__.ts
CHANGED
|
@@ -19,7 +19,8 @@ export * from './src/decorator/controller.ts';
|
|
|
19
19
|
export * from './src/decorator/param.ts';
|
|
20
20
|
export * from './src/decorator/endpoint.ts';
|
|
21
21
|
|
|
22
|
-
export * from './src/registry/
|
|
22
|
+
export * from './src/registry/registry-index.ts';
|
|
23
|
+
export * from './src/registry/registry-adapter.ts';
|
|
23
24
|
export * from './src/registry/visitor.ts';
|
|
24
25
|
export * from './src/registry/types.ts';
|
|
25
26
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/web",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Declarative support creating
|
|
3
|
+
"version": "7.0.0-rc.0",
|
|
4
|
+
"description": "Declarative support for creating Web Applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"web",
|
|
7
7
|
"decorators",
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
"directory": "module/web"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@travetto/config": "^
|
|
29
|
-
"@travetto/context": "^
|
|
30
|
-
"@travetto/di": "^
|
|
31
|
-
"@travetto/registry": "^
|
|
32
|
-
"@travetto/runtime": "^
|
|
33
|
-
"@travetto/schema": "^
|
|
28
|
+
"@travetto/config": "^7.0.0-rc.0",
|
|
29
|
+
"@travetto/context": "^7.0.0-rc.0",
|
|
30
|
+
"@travetto/di": "^7.0.0-rc.0",
|
|
31
|
+
"@travetto/registry": "^7.0.0-rc.0",
|
|
32
|
+
"@travetto/runtime": "^7.0.0-rc.0",
|
|
33
|
+
"@travetto/schema": "^7.0.0-rc.0",
|
|
34
34
|
"find-my-way": "^9.3.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"@travetto/cli": "^
|
|
38
|
-
"@travetto/test": "^
|
|
39
|
-
"@travetto/transformer": "^
|
|
37
|
+
"@travetto/cli": "^7.0.0-rc.0",
|
|
38
|
+
"@travetto/test": "^7.0.0-rc.0",
|
|
39
|
+
"@travetto/transformer": "^7.0.0-rc.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@travetto/transformer": {
|
package/src/common/global.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DependencyRegistryIndex } from '@travetto/di';
|
|
2
2
|
import { Runtime } from '@travetto/runtime';
|
|
3
|
+
import { IsPrivate } from '@travetto/schema';
|
|
3
4
|
|
|
4
5
|
import { Controller } from '../decorator/controller.ts';
|
|
5
|
-
import { ConditionalRegister, ConfigureInterceptor
|
|
6
|
+
import { ConditionalRegister, ConfigureInterceptor } from '../decorator/common.ts';
|
|
6
7
|
import { Get, Options } from '../decorator/endpoint.ts';
|
|
7
8
|
import { WebConfig } from '../config.ts';
|
|
8
9
|
import { LoggingInterceptor, } from '../interceptor/logging.ts';
|
|
9
10
|
|
|
10
|
-
@
|
|
11
|
+
@IsPrivate()
|
|
11
12
|
@Controller('/')
|
|
12
13
|
@ConfigureInterceptor(LoggingInterceptor, { applies: false })
|
|
13
14
|
export class GlobalHandler {
|
|
14
15
|
|
|
15
16
|
@Get('')
|
|
16
17
|
@ConditionalRegister(async () => {
|
|
17
|
-
const config = await
|
|
18
|
+
const config = await DependencyRegistryIndex.getInstance(WebConfig);
|
|
18
19
|
return config.defaultMessage;
|
|
19
20
|
})
|
|
20
21
|
message(): { module: string, version: string, env?: string } {
|
package/src/decorator/common.ts
CHANGED
|
@@ -1,34 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Class, ClassInstance, getClass, RetainPrimitiveFields, TimeSpan, TimeUtil } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { EndpointConfig, ControllerConfig,
|
|
3
|
+
import { ControllerRegistryIndex } from '../registry/registry-index.ts';
|
|
4
|
+
import { EndpointConfig, ControllerConfig, EndpointDecorator, EndpointFunctionDescriptor } from '../registry/types.ts';
|
|
5
5
|
import { AcceptInterceptor } from '../interceptor/accept.ts';
|
|
6
6
|
import { WebInterceptor } from '../types/interceptor.ts';
|
|
7
7
|
|
|
8
|
+
function isClass(target: unknown, property: unknown,): target is Class<unknown> {
|
|
9
|
+
return !property;
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
function register(config: Partial<EndpointConfig | ControllerConfig>): EndpointDecorator {
|
|
9
|
-
return function <T>(
|
|
10
|
-
|
|
11
|
-
|
|
13
|
+
return function <T>(instanceOrCls: ClassInstance | Class<T>, property?: string | symbol, _?: EndpointFunctionDescriptor) {
|
|
14
|
+
const adapter = ControllerRegistryIndex.getForRegister(getClass(instanceOrCls));
|
|
15
|
+
if (isClass(instanceOrCls, property)) {
|
|
16
|
+
adapter.register(config);
|
|
12
17
|
} else {
|
|
13
|
-
|
|
18
|
+
adapter.registerEndpoint(property!, config);
|
|
14
19
|
}
|
|
15
20
|
};
|
|
16
21
|
}
|
|
17
22
|
|
|
18
|
-
/**
|
|
19
|
-
* Decorator used to add description metadata to a class or method
|
|
20
|
-
* @param desc The describe config
|
|
21
|
-
*/
|
|
22
|
-
export function Describe(desc: DescribableConfig): EndpointDecorator { return register(desc); }
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Marks a class/endpoint as being undocumented
|
|
26
|
-
*/
|
|
27
|
-
export function Undocumented(): EndpointDecorator { return register({ documented: false }); }
|
|
28
|
-
|
|
29
23
|
/**
|
|
30
24
|
* Set response headers on success
|
|
31
25
|
* @param headers The response headers to set
|
|
26
|
+
* @kind decorator
|
|
32
27
|
*/
|
|
33
28
|
export function SetHeaders(headers: EndpointConfig['responseHeaders']): EndpointDecorator {
|
|
34
29
|
return register({ responseHeaders: headers });
|
|
@@ -36,6 +31,7 @@ export function SetHeaders(headers: EndpointConfig['responseHeaders']): Endpoint
|
|
|
36
31
|
|
|
37
32
|
/**
|
|
38
33
|
* Specifies content type for response
|
|
34
|
+
* @kind decorator
|
|
39
35
|
*/
|
|
40
36
|
export function Produces(mime: string): EndpointDecorator { return SetHeaders({ 'Content-Type': mime }); }
|
|
41
37
|
|
|
@@ -44,6 +40,7 @@ type CacheControlInput = { cacheableAge?: number | TimeSpan, isPrivate?: boolean
|
|
|
44
40
|
/**
|
|
45
41
|
* Set the max-age of a response based on the config
|
|
46
42
|
* @param value The value for the duration
|
|
43
|
+
* @kind decorator
|
|
47
44
|
*/
|
|
48
45
|
export function CacheControl(input: TimeSpan | number | CacheControlInput, extra?: Omit<CacheControlInput, 'cacheableAge'>): EndpointDecorator {
|
|
49
46
|
if (typeof input === 'string' || typeof input === 'number') {
|
|
@@ -61,9 +58,10 @@ export function CacheControl(input: TimeSpan | number | CacheControlInput, extra
|
|
|
61
58
|
/**
|
|
62
59
|
* Define an endpoint to support specific input types
|
|
63
60
|
* @param types The list of mime types to allow/deny
|
|
61
|
+
* @kind decorator
|
|
64
62
|
*/
|
|
65
63
|
export function Accepts(types: [string, ...string[]]): EndpointDecorator {
|
|
66
|
-
return
|
|
64
|
+
return ControllerRegistryIndex.createInterceptorConfigDecorator(
|
|
67
65
|
AcceptInterceptor,
|
|
68
66
|
{ types, applies: true },
|
|
69
67
|
{ responseHeaders: { accepts: types.join(', ') } }
|
|
@@ -72,12 +70,18 @@ export function Accepts(types: [string, ...string[]]): EndpointDecorator {
|
|
|
72
70
|
|
|
73
71
|
/**
|
|
74
72
|
* Allows for configuring interceptor-level support at an endpoint or controller level
|
|
73
|
+
* @kind decorator
|
|
75
74
|
*/
|
|
76
|
-
export const ConfigureInterceptor =
|
|
77
|
-
|
|
75
|
+
export const ConfigureInterceptor = <T extends WebInterceptor>(
|
|
76
|
+
cls: Class<T>,
|
|
77
|
+
cfg: Partial<RetainPrimitiveFields<T['config']>>,
|
|
78
|
+
extra?: Partial<EndpointConfig & ControllerConfig>
|
|
79
|
+
): EndpointDecorator =>
|
|
80
|
+
ControllerRegistryIndex.createInterceptorConfigDecorator(cls, cfg, extra);
|
|
78
81
|
|
|
79
82
|
/**
|
|
80
83
|
* Specifies if endpoint should be conditional
|
|
84
|
+
* @kind decorator
|
|
81
85
|
*/
|
|
82
86
|
export function ConditionalRegister(handler: () => (boolean | Promise<boolean>)): EndpointDecorator {
|
|
83
87
|
return register({ conditional: handler });
|
|
@@ -85,6 +89,7 @@ export function ConditionalRegister(handler: () => (boolean | Promise<boolean>))
|
|
|
85
89
|
|
|
86
90
|
/**
|
|
87
91
|
* Registers an interceptor exclusion filter
|
|
92
|
+
* @kind decorator
|
|
88
93
|
*/
|
|
89
94
|
export function ExcludeInterceptors(interceptorExclude: (val: WebInterceptor) => boolean): EndpointDecorator {
|
|
90
95
|
return register({ interceptorExclude });
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Class } from '@travetto/runtime';
|
|
2
|
-
import {
|
|
2
|
+
import { DependencyRegistryIndex } from '@travetto/di';
|
|
3
|
+
|
|
4
|
+
import { ControllerRegistryIndex } from '../registry/registry-index.ts';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Decorator to register a new web controller
|
|
6
|
-
* @augments `@travetto/
|
|
7
|
-
* @
|
|
8
|
+
* @augments `@travetto/schema:Schema`
|
|
9
|
+
* @kind decorator
|
|
8
10
|
*/
|
|
9
11
|
export function Controller(path: string) {
|
|
10
|
-
return function <T>(
|
|
11
|
-
|
|
12
|
+
return function <T>(cls: Class<T>): void {
|
|
13
|
+
ControllerRegistryIndex.getForRegister(cls).register({ basePath: path, class: cls, });
|
|
14
|
+
DependencyRegistryIndex.getForRegister(cls).registerClass();
|
|
12
15
|
};
|
|
13
16
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClassInstance, getClass } from '@travetto/runtime';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import { EndpointConfig, EndpointFunctionDescriptor, EndpointIOType } from '../registry/types.ts';
|
|
3
|
+
import { EndpointConfig, EndpointFunctionDescriptor } from '../registry/types.ts';
|
|
5
4
|
import { HTTP_METHODS, HttpMethod } from '../types/core.ts';
|
|
5
|
+
import { ControllerRegistryIndex } from '../registry/registry-index.ts';
|
|
6
6
|
|
|
7
|
-
type EndpointFunctionDecorator = <T>(
|
|
7
|
+
type EndpointFunctionDecorator = <T>(instance: T, property: symbol | string, descriptor: EndpointFunctionDescriptor) => EndpointFunctionDescriptor;
|
|
8
8
|
|
|
9
9
|
type EndpointDecConfig = Partial<EndpointConfig> & { path: string };
|
|
10
10
|
|
|
@@ -12,11 +12,9 @@ type EndpointDecConfig = Partial<EndpointConfig> & { path: string };
|
|
|
12
12
|
* Generic Endpoint Decorator
|
|
13
13
|
*/
|
|
14
14
|
export function Endpoint(config: EndpointDecConfig): EndpointFunctionDecorator {
|
|
15
|
-
return function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
return result;
|
|
15
|
+
return function (instance: ClassInstance, property: symbol | string, descriptor: EndpointFunctionDescriptor): EndpointFunctionDescriptor {
|
|
16
|
+
ControllerRegistryIndex.getForRegister(getClass(instance)).registerEndpoint(property, { methodName: property }, config);
|
|
17
|
+
return descriptor;
|
|
20
18
|
};
|
|
21
19
|
}
|
|
22
20
|
|
|
@@ -37,65 +35,49 @@ function HttpEndpoint(method: HttpMethod, path: string): EndpointFunctionDecorat
|
|
|
37
35
|
/**
|
|
38
36
|
* Registers GET requests
|
|
39
37
|
* @param path The endpoint path for the request
|
|
40
|
-
* @augments `@travetto/
|
|
38
|
+
* @augments `@travetto/schema:Method`
|
|
39
|
+
* @kind decorator
|
|
41
40
|
*/
|
|
42
41
|
export function Get(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('GET', path); }
|
|
43
42
|
/**
|
|
44
43
|
* Registers POST requests
|
|
45
44
|
* @param path The endpoint path for the request
|
|
46
|
-
* @augments `@travetto/
|
|
47
|
-
* @
|
|
45
|
+
* @augments `@travetto/schema:Method`
|
|
46
|
+
* @kind decorator
|
|
48
47
|
*/
|
|
49
48
|
export function Post(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('POST', path); }
|
|
50
49
|
/**
|
|
51
50
|
* Registers PUT requests
|
|
52
51
|
* @param path The endpoint path for the request
|
|
53
|
-
* @augments `@travetto/
|
|
54
|
-
* @
|
|
52
|
+
* @augments `@travetto/schema:Method`
|
|
53
|
+
* @kind decorator
|
|
55
54
|
*/
|
|
56
55
|
export function Put(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('PUT', path); }
|
|
57
56
|
/**
|
|
58
57
|
* Registers PATCH requests
|
|
59
58
|
* @param path The endpoint path for the request
|
|
60
|
-
* @augments `@travetto/
|
|
61
|
-
* @
|
|
59
|
+
* @augments `@travetto/schema:Method`
|
|
60
|
+
* @kind decorator
|
|
62
61
|
*/
|
|
63
62
|
export function Patch(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('PATCH', path); }
|
|
64
63
|
/**
|
|
65
64
|
* Registers DELETE requests
|
|
66
65
|
* @param path The endpoint path for the request
|
|
67
|
-
* @augments `@travetto/
|
|
66
|
+
* @augments `@travetto/schema:Method`
|
|
67
|
+
* @kind decorator
|
|
68
68
|
*/
|
|
69
69
|
export function Delete(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('DELETE', path); }
|
|
70
70
|
/**
|
|
71
71
|
* Registers HEAD requests
|
|
72
72
|
* @param path The endpoint path for the request
|
|
73
|
-
* @augments `@travetto/
|
|
73
|
+
* @augments `@travetto/schema:Method`
|
|
74
|
+
* @kind decorator
|
|
74
75
|
*/
|
|
75
76
|
export function Head(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('HEAD', path); }
|
|
76
77
|
/**
|
|
77
78
|
* Registers OPTIONS requests
|
|
78
79
|
* @param path The endpoint path for the request
|
|
79
|
-
* @augments `@travetto/
|
|
80
|
+
* @augments `@travetto/schema:Method`
|
|
81
|
+
* @kind decorator
|
|
80
82
|
*/
|
|
81
83
|
export function Options(path = '/'): EndpointFunctionDecorator { return HttpEndpoint('OPTIONS', path); }
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Defines the response type of the endpoint
|
|
85
|
-
* @param responseType The desired response mime type
|
|
86
|
-
*/
|
|
87
|
-
export function ResponseType(responseType: EndpointIOType): EndpointFunctionDecorator {
|
|
88
|
-
return function <T>(target: T, property: string | symbol, descriptor: EndpointFunctionDescriptor) {
|
|
89
|
-
return ControllerRegistry.registerPendingEndpoint(asConstructable(target).constructor, descriptor, { responseType });
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Defines the supported request body type
|
|
95
|
-
* @param requestType The type of the request body
|
|
96
|
-
*/
|
|
97
|
-
export function RequestType(requestType: EndpointIOType): EndpointFunctionDecorator {
|
|
98
|
-
return function <T>(target: T, property: string | symbol, descriptor: EndpointFunctionDescriptor) {
|
|
99
|
-
return ControllerRegistry.registerPendingEndpoint(asConstructable(target).constructor, descriptor, { requestType });
|
|
100
|
-
};
|
|
101
|
-
}
|
package/src/decorator/param.ts
CHANGED
|
@@ -1,64 +1,71 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClassInstance, getClass } from '@travetto/runtime';
|
|
2
|
+
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { ControllerRegistryIndex } from '../registry/registry-index.ts';
|
|
5
|
+
import { EndpointParameterConfig, EndpointParamLocation } from '../registry/types.ts';
|
|
5
6
|
|
|
6
|
-
type ParamDecorator = (
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get the param configuration
|
|
10
|
-
* @param location The location of the parameter
|
|
11
|
-
* @param extra Any additional configuration for the param config
|
|
12
|
-
*/
|
|
13
|
-
export const paramConfig = (location: EndpointParamConfig['location'], extra: string | Partial<EndpointParamConfig>): EndpointParamConfig => ({
|
|
14
|
-
location,
|
|
15
|
-
...((typeof extra === 'string' ? { name: extra } : extra))
|
|
16
|
-
});
|
|
7
|
+
type ParamDecorator = (instance: ClassInstance, property: string | symbol, idx: number) => void;
|
|
17
8
|
|
|
18
9
|
/**
|
|
19
10
|
* Define a parameter
|
|
20
11
|
* @param location The location of the parameter
|
|
21
12
|
* @param extra Any extra configuration for the param
|
|
22
|
-
* @augments `@travetto/
|
|
13
|
+
* @augments `@travetto/schema:Input`
|
|
14
|
+
* @kind decorator
|
|
23
15
|
*/
|
|
24
|
-
export function Param(location:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
16
|
+
export function Param(location: EndpointParamLocation, extra: string | Partial<EndpointParameterConfig>): ParamDecorator {
|
|
17
|
+
return (instance: ClassInstance, property: string | symbol, idx: number): void => {
|
|
18
|
+
const name = typeof extra === 'string' ? extra : extra.name;
|
|
19
|
+
const config = typeof extra === 'string' ? {} : extra;
|
|
20
|
+
|
|
21
|
+
// Set name as needed
|
|
22
|
+
if (name) {
|
|
23
|
+
SchemaRegistryIndex.getForRegister(getClass(instance)).registerParameter(property, idx, { name });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ControllerRegistryIndex.getForRegister(getClass(instance)).registerEndpointParameter(property, idx, {
|
|
27
|
+
index: idx, location, ...config
|
|
28
|
+
});
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
33
|
* Define a Path param
|
|
34
34
|
* @param param The param configuration or name
|
|
35
|
-
* @augments `@travetto/
|
|
35
|
+
* @augments `@travetto/schema:Input`
|
|
36
|
+
* @kind decorator
|
|
36
37
|
*/
|
|
37
|
-
export function PathParam(param: string | Partial<
|
|
38
|
+
export function PathParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('path', param); }
|
|
38
39
|
/**
|
|
39
40
|
* Define a Query param
|
|
40
41
|
* @param param The param configuration or name
|
|
41
|
-
* @augments `@travetto/
|
|
42
|
+
* @augments `@travetto/schema:Input`
|
|
43
|
+
* @kind decorator
|
|
42
44
|
*/
|
|
43
|
-
export function QueryParam(param: string | Partial<
|
|
45
|
+
export function QueryParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('query', param); }
|
|
44
46
|
/**
|
|
45
47
|
* Define a Header param
|
|
46
48
|
* @param param The param configuration or name
|
|
47
|
-
* @augments `@travetto/
|
|
49
|
+
* @augments `@travetto/schema:Input`
|
|
50
|
+
* @kind decorator
|
|
48
51
|
*/
|
|
49
|
-
export function HeaderParam(param: string | Partial<
|
|
52
|
+
export function HeaderParam(param: string | Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('header', param); }
|
|
50
53
|
/**
|
|
51
54
|
* Define a body param as an input
|
|
52
55
|
* @param param The param configuration
|
|
53
|
-
* @augments `@travetto/
|
|
56
|
+
* @augments `@travetto/schema:Input`
|
|
57
|
+
* @kind decorator
|
|
54
58
|
*/
|
|
55
|
-
export function Body(param: Partial<
|
|
59
|
+
export function Body(param: Partial<EndpointParameterConfig> = {}): ParamDecorator { return Param('body', param); }
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
62
|
* A contextual field as provided by the WebAsyncContext
|
|
59
|
-
* @augments `@travetto/
|
|
63
|
+
* @augments `@travetto/schema:Field`
|
|
64
|
+
* @kind decorator
|
|
60
65
|
*/
|
|
61
|
-
export function ContextParam(
|
|
62
|
-
return (
|
|
63
|
-
|
|
66
|
+
export function ContextParam() {
|
|
67
|
+
return (instance: ClassInstance, property: string | symbol): void => {
|
|
68
|
+
ControllerRegistryIndex.getForRegister(getClass(instance)).register({ contextParams: { [property]: true } });
|
|
69
|
+
ControllerRegistryIndex.bindContextParamsOnPostConstruct(getClass(instance));
|
|
70
|
+
};
|
|
64
71
|
}
|
package/src/interceptor/body.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Injectable, Inject,
|
|
1
|
+
import { Injectable, Inject, DependencyRegistryIndex } from '@travetto/di';
|
|
2
2
|
import { Config } from '@travetto/config';
|
|
3
3
|
import { toConcrete } from '@travetto/runtime';
|
|
4
4
|
import { Ignore } from '@travetto/schema';
|
|
@@ -66,7 +66,7 @@ export class BodyInterceptor implements WebInterceptor<WebBodyConfig> {
|
|
|
66
66
|
|
|
67
67
|
async postConstruct(): Promise<void> {
|
|
68
68
|
// Load all the parser types
|
|
69
|
-
const instances = await
|
|
69
|
+
const instances = await DependencyRegistryIndex.getInstances(toConcrete<BodyContentParser>());
|
|
70
70
|
for (const instance of instances) {
|
|
71
71
|
this.parsers[instance.type] = instance;
|
|
72
72
|
}
|
|
@@ -96,8 +96,8 @@ export class BodyInterceptor implements WebInterceptor<WebBodyConfig> {
|
|
|
96
96
|
return next();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
const [
|
|
100
|
-
const parserType = config.parsingTypes[contentType.value] ?? config.parsingTypes[
|
|
99
|
+
const [baseMimeType,] = contentType.value.split('/');
|
|
100
|
+
const parserType = config.parsingTypes[contentType.value] ?? config.parsingTypes[baseMimeType];
|
|
101
101
|
if (!parserType) {
|
|
102
102
|
return next();
|
|
103
103
|
}
|