Fix doc for route and move autoRoute to rest library
This commit is contained in:
Родитель
9b087aebc8
Коммит
840867dd83
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@cadl-lang/rest",
|
||||
"comment": "Fix doc for route and move autoRoute to rest library",
|
||||
"type": "minor"
|
||||
}
|
||||
],
|
||||
"packageName": "@cadl-lang/rest"
|
||||
}
|
|
@ -296,9 +296,9 @@ In Cadl this information is specified with [decorators on the namespace][cadl-se
|
|||
|
||||
| OpenAPI `info` field | Cadl decorator | Notes |
|
||||
| -------------------- | ----------------- | ------------------------ |
|
||||
| `title` | `@serviceTitle` | |
|
||||
| `version` | `@serviceVersion` | |
|
||||
| `description` | | Not currently supported. |
|
||||
| `title` | `@serviceTitle` | Cadl built-in decorator |
|
||||
| `version` | `@serviceVersion` | Cadl built-in decorator |
|
||||
| `description` | `@doc` | Cadl built-in decorator |
|
||||
| `license` | | Not currently supported. |
|
||||
| `contact` | | Not currently supported. |
|
||||
|
||||
|
|
|
@ -75,21 +75,22 @@ See [Rest section in the tutorial](https://github.com/microsoft/cadl/blob/main/d
|
|||
|
||||
The `@cadl-lang/rest` library defines the following decorators in `Cadl.Http` namespace:
|
||||
|
||||
| Declarator | Scope | Usage |
|
||||
| ----------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| @get | operations | indicating operation uses HTTP `GET` verb. |
|
||||
| @put | operations | indicating operation uses HTTP `PUT` verb. |
|
||||
| @post | operations | indicating operation uses HTTP `POST` verb. |
|
||||
| @patch | operations | indicating operation uses HTTP `PATCH` verb. |
|
||||
| @delete | operations | indicating operation uses HTTP `DEL` verb. |
|
||||
| @head | operations | indicating operation uses HTTP `HEAD` verb. |
|
||||
| @header | model properties and operation parameters | indicating the properties are request or response headers. |
|
||||
| @query | model properties and operation parameters | indicating the properties are in the request query string. |
|
||||
| @body | model properties and operation parameters | indicating the property is in request or response body. Only one allowed per model and operation. |
|
||||
| @path | model properties and operation parameters | indicating the properties are in request path. |
|
||||
| @statusCode | model properties and operation parameters | indicating the property is the return status code. Only one allowed per model. |
|
||||
| @server | namespace | Configure the server url for the service. |
|
||||
| @useAuth | namespace | Configure the service authentication. |
|
||||
| Declarator | Scope | Usage |
|
||||
| ----------- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| @get | operations | indicating operation uses HTTP `GET` verb. |
|
||||
| @put | operations | indicating operation uses HTTP `PUT` verb. |
|
||||
| @post | operations | indicating operation uses HTTP `POST` verb. |
|
||||
| @patch | operations | indicating operation uses HTTP `PATCH` verb. |
|
||||
| @delete | operations | indicating operation uses HTTP `DEL` verb. |
|
||||
| @head | operations | indicating operation uses HTTP `HEAD` verb. |
|
||||
| @header | model properties and operation parameters | indicating the properties are request or response headers. |
|
||||
| @query | model properties and operation parameters | indicating the properties are in the request query string. |
|
||||
| @body | model properties and operation parameters | indicating the property is in request or response body. Only one allowed per model and operation. |
|
||||
| @path | model properties and operation parameters | indicating the properties are in request path. |
|
||||
| @statusCode | model properties and operation parameters | indicating the property is the return status code. Only one allowed per model. |
|
||||
| @server | namespace | Configure the server url for the service. |
|
||||
| @route | operations, namespaces, interfaces | Syntax:<br> `@route(routeString)`<br><br>Note:<br>`@route` defines the relative route URI for the target operation. The `routeString` argument should be a URI fragment that may contain one or more path parameter fields. If the namespace or interface that contains the operation is also marked with a `@route` decorator, it will be used as a prefix to the route URI of the operation. |
|
||||
| @useAuth | namespace | Configure the service authentication. |
|
||||
|
||||
- ### REST namespace
|
||||
|
||||
|
@ -111,7 +112,6 @@ The `@cadl-lang/rest` library defines the following decorators in `Cadl.Rest` na
|
|||
| @segment | model properties, operation parameters | Syntax:<br> `@segment(segmentString)` <br><br>Note:<br>`@segment` defines the preceding path segment for a `@path` parameter in auto-generated routes. The first argument should be a string that will be inserted into the operation route before the path parameter's name field. For exmaple: <br> `op getUser(@path @segment("users") userId: string): User` <br> will produce the route `/users/{userId}`. |
|
||||
| @segmentOf | models | Syntax:<br> `@segment(segmentString)` <br><br>Note:<br>`@segmentOf` returns the URL segment of a given model if it has `@segment` and `@key` decorator. |
|
||||
| @segmentSeparator | model properties, operation parameters, or operations | Syntax:<br> `@segmentSeparator(separatorString)` <br><br>Note:<br> `@segmentSeparator` defines the separator string that is inserted between the target's `@segment` and the preceding route path in auto-generated routes. <br> The first argument should be a string that will be inserted into the operation route before the target's `@segment` value. Can be a string of any length. Defaults to `/`. |
|
||||
| @route | operations, namespaces, interfaces | Syntax:<br> `@route(routeString)` <br><br>Note:<br> `@route` defines the relative route URI for the target operation The first argument should be a URI fragment that may contain one or more path parameter fields.If the namespace or interface that contains the operation is also marked with a `@route` decorator, it will be used as a prefix to the route URI of the operation. |
|
||||
| @autoRoute | operations | Syntax:<br> `@autoRoute()` <br><br>Note:<br>`@autoRoute` enables automatic route generation for an operation, namespace, or interface. <br> When applied to an operation, it automatically generates the operation's route based on path parameter metadata. When applied to a namespace or interface, it causes all operations under that scope to have auto-generated routes. |
|
||||
|
||||
## Interfaces
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
getResourceOperation,
|
||||
getSegment,
|
||||
getSegmentSeparator,
|
||||
isAutoRoute,
|
||||
} from "../rest.js";
|
||||
import { extractParamsFromPath } from "../utils.js";
|
||||
import {
|
||||
|
@ -597,42 +598,3 @@ const resourceOperationToVerb: any = {
|
|||
delete: "delete",
|
||||
list: "get",
|
||||
};
|
||||
|
||||
const autoRouteKey = createStateSymbol("autoRoute");
|
||||
|
||||
/**
|
||||
* `@autoRoute` enables automatic route generation for an operation, namespace, or interface.
|
||||
*
|
||||
* When applied to an operation, it automatically generates the operation's route based on path parameter
|
||||
* metadata. When applied to a namespace or interface, it causes all operations under that scope to have
|
||||
* auto-generated routes.
|
||||
*/
|
||||
export function $autoRoute(context: DecoratorContext, entity: Type) {
|
||||
if (
|
||||
!validateDecoratorTarget(context, entity, "@autoRoute", ["Namespace", "Interface", "Operation"])
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.program.stateSet(autoRouteKey).add(entity);
|
||||
}
|
||||
|
||||
export function isAutoRoute(program: Program, target: Namespace | Interface | Operation): boolean {
|
||||
// Loop up through parent scopes (interface, namespace) to see if
|
||||
// @autoRoute was used anywhere
|
||||
let current: Namespace | Interface | Operation | undefined = target;
|
||||
while (current !== undefined) {
|
||||
if (program.stateSet(autoRouteKey).has(current)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Navigate up to the parent scope
|
||||
if (current.kind === "Namespace" || current.kind === "Interface") {
|
||||
current = current.namespace;
|
||||
} else if (current.kind === "Operation") {
|
||||
current = current.interface || current.namespace;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
createDecoratorDefinition,
|
||||
DecoratorContext,
|
||||
DecoratorValidator,
|
||||
Interface,
|
||||
Model,
|
||||
ModelProperty,
|
||||
Namespace,
|
||||
|
@ -158,6 +159,58 @@ export function getDiscriminator(program: Program, entity: Type): Discriminator
|
|||
return undefined;
|
||||
}
|
||||
|
||||
// ----------------- @autoRoute -----------------
|
||||
|
||||
const autoRouteDecorator = createDecoratorDefinition({
|
||||
name: "@autoRoute",
|
||||
target: ["Namespace", "Interface", "Operation"],
|
||||
args: [],
|
||||
} as const);
|
||||
|
||||
const autoRouteKey = createStateSymbol("autoRoute");
|
||||
|
||||
/**
|
||||
* `@autoRoute` enables automatic route generation for an operation, namespace, or interface.
|
||||
*
|
||||
* When applied to an operation, it automatically generates the operation's route based on path parameter
|
||||
* metadata. When applied to a namespace or interface, it causes all operations under that scope to have
|
||||
* auto-generated routes.
|
||||
*/
|
||||
|
||||
export function $autoRoute(
|
||||
context: DecoratorContext,
|
||||
entity: Namespace | Interface | Operation,
|
||||
...args: readonly []
|
||||
) {
|
||||
if (!autoRouteDecorator.validate(context, entity, args)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.program.stateSet(autoRouteKey).add(entity);
|
||||
}
|
||||
|
||||
export function isAutoRoute(program: Program, target: Namespace | Interface | Operation): boolean {
|
||||
// Loop up through parent scopes (interface, namespace) to see if
|
||||
// @autoRoute was used anywhere
|
||||
let current: Namespace | Interface | Operation | undefined = target;
|
||||
while (current !== undefined) {
|
||||
if (program.stateSet(autoRouteKey).has(current)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Navigate up to the parent scope
|
||||
if (current.kind === "Namespace" || current.kind === "Interface") {
|
||||
current = current.namespace;
|
||||
} else if (current.kind === "Operation") {
|
||||
current = current.interface || current.namespace;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ------------------ @segment ------------------
|
||||
|
||||
const segmentDecorator = createDecoratorDefinition({
|
||||
name: "@segment",
|
||||
target: ["Model", "ModelProperty", "Operation"],
|
||||
|
|
|
@ -17,13 +17,13 @@ import {
|
|||
isQueryParam,
|
||||
isStatusCode,
|
||||
} from "../src/http/decorators.js";
|
||||
import { createRestTestRunner } from "./test-host.js";
|
||||
import { createHttpTestRunner } from "./test-host.js";
|
||||
|
||||
describe("rest: http decorators", () => {
|
||||
let runner: BasicTestRunner;
|
||||
|
||||
beforeEach(async () => {
|
||||
runner = await createRestTestRunner();
|
||||
runner = await createHttpTestRunner();
|
||||
});
|
||||
|
||||
describe("emit diagnostic if passing arguments to verb decorators", () => {
|
||||
|
|
|
@ -409,6 +409,17 @@ describe("rest: routes", () => {
|
|||
strictEqual(diagnostics[1].message, `Duplicate operation "get2" routed at "get /test".`);
|
||||
});
|
||||
|
||||
it("emit diagnostic if passing arguments to autoroute decorators", async () => {
|
||||
const [_, diagnostics] = await compileOperations(`
|
||||
@autoRoute("/test") op test(): string;
|
||||
`);
|
||||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument-count",
|
||||
message: "Expected 0 arguments, but got 1.",
|
||||
});
|
||||
});
|
||||
|
||||
describe("operation parameters", () => {
|
||||
it("emit diagnostic for parameters with multiple http request annotations", async () => {
|
||||
const [_, diagnostics] = await compileOperations(`
|
||||
|
|
|
@ -20,6 +20,15 @@ export async function createRestTestHost(): Promise<TestHost> {
|
|||
libraries: [RestTestLibrary],
|
||||
});
|
||||
}
|
||||
export async function createHttpTestRunner(): Promise<BasicTestRunner> {
|
||||
const host = await createRestTestHost();
|
||||
return createTestWrapper(
|
||||
host,
|
||||
(code) =>
|
||||
`import "@cadl-lang/rest"; using Cadl.Http;
|
||||
${code}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function createRestTestRunner(): Promise<BasicTestRunner> {
|
||||
const host = await createRestTestHost();
|
||||
|
|
Загрузка…
Ссылка в новой задаче