Deprecate @consumes @produces (#995)
This commit is contained in:
Родитель
840867dd83
Коммит
4d0242c3d2
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"changes": [
|
||||
{
|
||||
"packageName": "@cadl-lang/rest",
|
||||
"comment": "**Deprecation**: Mark `@produces` and `@consumes` as deprecated",
|
||||
"type": "minor"
|
||||
}
|
||||
],
|
||||
"packageName": "@cadl-lang/rest"
|
||||
}
|
|
@ -824,8 +824,6 @@ A definition for a service is the namespace that contains all the operations for
|
|||
- @serviceTitle - the title of the service
|
||||
- @serviceVersion - the version of the service. Can be any string, but later version should lexicographically sort after earlier versions
|
||||
- @server - the host of the service. Can accept parameters.
|
||||
- @produces - the content types the service may produce
|
||||
- @consumes - the content types that may be sent to the service
|
||||
|
||||
Here's an example that uses these to define a Pet Store service:
|
||||
|
||||
|
@ -834,8 +832,6 @@ Here's an example that uses these to define a Pet Store service:
|
|||
@serviceVersion("2021-03-25")
|
||||
@server("https://example.com", "Single server endpoint")
|
||||
@doc("This is a sample server Petstore server.")
|
||||
@Cadl.Rest.produces("application/json", "image/png")
|
||||
@Cadl.Rest.consumes("application/json")
|
||||
namespace PetStore;
|
||||
```
|
||||
|
||||
|
|
|
@ -98,8 +98,6 @@ The `@cadl-lang/rest` library defines the following decorators in `Cadl.Rest` na
|
|||
|
||||
| Declarator | Scope | Syntax |
|
||||
| ------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| @produces | namespace, operations | Syntax:<br> `@produces(mimetypeString)` <br><br>Note:<br> The `@produces` decorator is used to specify the MIME media types or representations a resource can produce and send back to the client. |
|
||||
| @consumes | namespace, operations | Syntax:<br> `@consumes(mimetypeString)` <br><br>Note:<br> The `@consumes` decorator is used to specify which MIME media types of representations a resource can accept, or consume, from the client. |
|
||||
| @discriminator | models | Syntax:<br> `@discriminator(kindString)` <br><br>Note:<br> `@discriminator` allows defining polymorphic models to be used by API as parameters and return types. In many strongly typed languages, they are expressed as inheritance. |
|
||||
| @resource | Model | Syntax:<br> `@resource(collectionName)` <br><br>Note:<br> This decorator is to used to mark a model as a resource type with a name for the type's collection. |
|
||||
| @readsResource | operations | Syntax:<br> `@readsResource(modelType)` <br><br>Note:<br> This decorator is to used to signal the operation that is the Read operation for a particular resource. |
|
||||
|
@ -174,6 +172,27 @@ interface WidgetService
|
|||
| ExtensionResourceCollectionOperations<TExtension, TResource, TError> | Combines extension resource POST + LIST operations. |
|
||||
| ExtensionResourceOperations<TExtension, TResource, TError> | Combines extension resource instance and collection operations. Includes GET + PATCH + DEL + POST + LIST operations. |
|
||||
|
||||
## How to
|
||||
|
||||
### Specify content type
|
||||
|
||||
To specify the content type you can add a `@header contentType: <value>` in the operation parameter(For request content type) or return type(For response content type)
|
||||
|
||||
Example: return `application/png` byte body
|
||||
|
||||
```cadl
|
||||
op getPng(): {
|
||||
@header contentType: "application/png";
|
||||
@body _: bytes;
|
||||
};
|
||||
```
|
||||
|
||||
Example: expect `application/png` byte body
|
||||
|
||||
```cadl
|
||||
op getPng(@header contentType: "application/png", @body _: bytes): void;
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [HTTP example](https://cadlplayground.z22.web.core.windows.net/?c=aW1wb3J0ICJAY2FkbC1sYW5nL3Jlc3QiOwoKQHNlcnZpY2VUaXRsZSgiV2lkZ2V0IFPGFSIpCm5hbWVzcGFjZSBEZW1vxxg7CnVzaW5nIENhZGwuSHR0cDsKCm1vZGVsIMdAewogIEBrZXkgaWQ6IHN0cmluZzsKICB3ZWlnaHQ6IGludDMyxBFjb2xvcjogInJlZCIgfCAiYmx1ZSI7Cn0KCkBlcnJvcsdWRcQMxVVjb2Rly0BtZXNzYWdlymR9CgppbnRlcmbkALLmAI3nALTFP0DkAJ1saXN0KCk6xx9bXSB8xmHEUUByb3V0ZSgid8Uccy97aWR9IinGOHJlYWQoQHBhdGjrANfJSM1GcG9zdCBjcmVhdGUoQGJvZHkgxAXIK9Y0x3pjdXN0b21HZXTId8kR6gC0yjh9Cg%3D%3D):
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
Namespace,
|
||||
Operation,
|
||||
Program,
|
||||
reportDeprecated,
|
||||
setCadlNamespace,
|
||||
Type,
|
||||
Union,
|
||||
|
@ -27,7 +28,15 @@ const producesDecorator = createDecoratorDefinition({
|
|||
},
|
||||
} as const);
|
||||
|
||||
/**
|
||||
* @deprecated Use return type `@header contentType` property instead
|
||||
*/
|
||||
export function $produces(context: DecoratorContext, entity: Namespace, ...contentTypes: string[]) {
|
||||
reportDeprecated(
|
||||
context.program,
|
||||
"@produces is deprecated. It has no effect. Use @header contentType: <ContentType> instead in operation return type.",
|
||||
context.decoratorTarget
|
||||
);
|
||||
if (!producesDecorator.validate(context, entity, contentTypes)) {
|
||||
return;
|
||||
}
|
||||
|
@ -36,6 +45,9 @@ export function $produces(context: DecoratorContext, entity: Namespace, ...conte
|
|||
context.program.stateMap(producesTypesKey).set(entity, values.concat(contentTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Check return type `@header contentType` property instead
|
||||
*/
|
||||
export function getProduces(program: Program, entity: Type): string[] {
|
||||
return program.stateMap(producesTypesKey).get(entity) || [];
|
||||
}
|
||||
|
@ -49,7 +61,16 @@ const consumeDefinition = createDecoratorDefinition({
|
|||
kind: "String",
|
||||
},
|
||||
} as const);
|
||||
|
||||
/**
|
||||
* @deprecated Use parameters `@header contentType` instead
|
||||
*/
|
||||
export function $consumes(context: DecoratorContext, entity: Namespace, ...contentTypes: string[]) {
|
||||
reportDeprecated(
|
||||
context.program,
|
||||
"@produces is deprecated. It has no effect. Use @header contentType: <ContentType> instead in operation parameters.",
|
||||
context.decoratorTarget
|
||||
);
|
||||
if (!consumeDefinition.validate(context, entity, contentTypes)) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,6 +79,9 @@ export function $consumes(context: DecoratorContext, entity: Namespace, ...conte
|
|||
context.program.stateMap(consumesTypesKey).set(entity, values.concat(contentTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Check parameters `@header contentType` instead
|
||||
*/
|
||||
export function getConsumes(program: Program, entity: Type): string[] {
|
||||
return program.stateMap(consumesTypesKey).get(entity) || [];
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Model } from "@cadl-lang/compiler";
|
||||
import { BasicTestRunner, expectDiagnostics } from "@cadl-lang/compiler/testing";
|
||||
import { deepStrictEqual, ok, strictEqual } from "assert";
|
||||
import { getConsumes, getProduces, getResourceLocationType } from "../src/rest.js";
|
||||
import { ok, strictEqual } from "assert";
|
||||
import { getResourceLocationType } from "../src/rest.js";
|
||||
import { createRestTestRunner } from "./test-host.js";
|
||||
|
||||
describe("rest: http decorators", () => {
|
||||
|
@ -11,116 +11,6 @@ describe("rest: http decorators", () => {
|
|||
runner = await createRestTestRunner();
|
||||
});
|
||||
|
||||
describe("@consumes", () => {
|
||||
it("emit diagnostics when @consumes is not used on namespace", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
@consumes op test(): string;
|
||||
|
||||
@consumes model Foo {}
|
||||
`);
|
||||
|
||||
expectDiagnostics(diagnostics, [
|
||||
{
|
||||
code: "decorator-wrong-target",
|
||||
message: "Cannot apply @consumes decorator to Operation",
|
||||
},
|
||||
{
|
||||
code: "decorator-wrong-target",
|
||||
message: "Cannot apply @consumes decorator to Model",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("emit diagnostics when parameter is not a string", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
@consumes(123)
|
||||
namespace Foo {}
|
||||
`);
|
||||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' of type 'Number' is not assignable to parameter of type 'String'",
|
||||
});
|
||||
});
|
||||
|
||||
it("allows a single content type", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
@consumes("application/json")
|
||||
@test namespace Foo {}
|
||||
`);
|
||||
|
||||
deepStrictEqual(getConsumes(runner.program, Foo), ["application/json"]);
|
||||
});
|
||||
|
||||
it("allows a multiple content type", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
@consumes("application/json", "application/xml", "application/yaml")
|
||||
@test namespace Foo {}
|
||||
`);
|
||||
|
||||
deepStrictEqual(getConsumes(runner.program, Foo), [
|
||||
"application/json",
|
||||
"application/xml",
|
||||
"application/yaml",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("@produces", () => {
|
||||
it("emit diagnostics when @produces is not used on namespace", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
@produces op test(): string;
|
||||
|
||||
@produces model Foo {}
|
||||
`);
|
||||
|
||||
expectDiagnostics(diagnostics, [
|
||||
{
|
||||
code: "decorator-wrong-target",
|
||||
message: "Cannot apply @produces decorator to Operation",
|
||||
},
|
||||
{
|
||||
code: "decorator-wrong-target",
|
||||
message: "Cannot apply @produces decorator to Model",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("emit diagnostics when parameter is not a string", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
@produces(123)
|
||||
namespace Foo {}
|
||||
`);
|
||||
|
||||
expectDiagnostics(diagnostics, {
|
||||
code: "invalid-argument",
|
||||
message: "Argument '123' of type 'Number' is not assignable to parameter of type 'String'",
|
||||
});
|
||||
});
|
||||
|
||||
it("allows a single content type", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
@produces("application/json")
|
||||
@test namespace Foo {}
|
||||
`);
|
||||
|
||||
deepStrictEqual(getProduces(runner.program, Foo), ["application/json"]);
|
||||
});
|
||||
|
||||
it("allows a multiple content type", async () => {
|
||||
const { Foo } = await runner.compile(`
|
||||
@produces("application/json", "application/xml", "application/yaml")
|
||||
@test namespace Foo {}
|
||||
`);
|
||||
|
||||
deepStrictEqual(getProduces(runner.program, Foo), [
|
||||
"application/json",
|
||||
"application/xml",
|
||||
"application/yaml",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("@resourceLocation", () => {
|
||||
it("emit diagnostic when used on non-model", async () => {
|
||||
const diagnostics = await runner.diagnose(`
|
||||
|
|
Загрузка…
Ссылка в новой задаче