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

Update to Body consistency in http request #2945

Merged
merged 35 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9d8e483
Add new decorators
timotheeguerin Feb 20, 2024
9b12f77
add basic tests
timotheeguerin Feb 20, 2024
d2690b5
Handle response
timotheeguerin Feb 21, 2024
35d876f
progress
timotheeguerin Feb 21, 2024
f3e53f2
Differ if in explicit body
timotheeguerin Feb 21, 2024
5339b34
fix test
timotheeguerin Feb 21, 2024
9682059
remove console
timotheeguerin Feb 21, 2024
ac83064
Progress
timotheeguerin Feb 21, 2024
494d85d
Use common logic to resolve body in request and response
timotheeguerin Feb 21, 2024
96f488a
Handle body ignore
timotheeguerin Feb 21, 2024
25d99cc
update emptied test
timotheeguerin Feb 22, 2024
19dd91b
Fix
timotheeguerin Feb 22, 2024
8ea4f2b
docs regen
timotheeguerin Feb 22, 2024
937353a
fix
timotheeguerin Feb 22, 2024
e23e9f3
fix
timotheeguerin Feb 22, 2024
1410105
fix issue
timotheeguerin Feb 23, 2024
fe18ddf
revert doc change
timotheeguerin Feb 23, 2024
836430a
Merge branch 'main' of https://github.com/microsoft/typespec into bod…
timotheeguerin Feb 28, 2024
7353bb8
Reduce breaking changes
timotheeguerin Feb 28, 2024
87fc5f3
Tweak
timotheeguerin Feb 28, 2024
e327a2f
Merge branch 'main' into body-consitency
timotheeguerin Feb 28, 2024
e40c50e
Merge branch 'main' into body-consitency
timotheeguerin Mar 6, 2024
e3a4655
Merge with main
timotheeguerin Mar 27, 2024
0578834
Merge branch 'main' into body-consitency
timotheeguerin Apr 2, 2024
859efeb
Create body-consitency-2024-2-27-18-35-44.md
timotheeguerin Apr 2, 2024
e9ef185
Create body-consitency-2024-2-27-18-35-44-1.md
timotheeguerin Apr 2, 2024
38dd669
Create body-consitency-2024-2-27-18-35-44-2.md
timotheeguerin Apr 2, 2024
d6be3d7
Update body-consitency-2024-2-27-18-35-44-2.md
timotheeguerin Apr 2, 2024
14b7d6b
Create body-consitency-2024-3-2-15-21-9.md
timotheeguerin Apr 2, 2024
a90480f
Create body-consitency-2024-3-2-15-21-10.md
timotheeguerin Apr 2, 2024
40eee18
Merge branch 'main' into body-consitency
timotheeguerin Apr 9, 2024
6822cf2
Merge branch 'main' into body-consitency
markcowl Apr 15, 2024
9ece16a
Update packages/http/src/metadata.ts
timotheeguerin Apr 15, 2024
46c8073
Merge branch 'main' into body-consitency
timotheeguerin Apr 16, 2024
a5d101d
Merge branch 'main' into body-consitency
timotheeguerin Apr 16, 2024
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
23 changes: 23 additions & 0 deletions .chronus/changes/body-consitency-2024-2-27-18-35-44-1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: breaking
packages:
- "@typespec/http"
---

Empty model after removing metadata and visibility property result in void always
This means the following case have changed from returning `{}` to no body

```tsp
op b1(): {};
op b2(): {@visibility("none") prop: string};
op b3(): {@added(Versions.v2) prop: string};
```

Workaround: Use explicit `@body`

```tsp
op b1(): {@body _: {}};
op b2(): {@body _: {@visibility("none") prop: string}};
op b3(): {@body _: {@added(Versions.v2) prop: string}};
```
18 changes: 18 additions & 0 deletions .chronus/changes/body-consitency-2024-2-27-18-35-44-2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: breaking
packages:
- "@typespec/http"
---

Implicit status code always 200 except if response is explicitly `void`

```tsp
op c1(): {@header foo: string}; // status code 200 (used to be 204)
```

Solution: Add explicit `@statusCode`
```tsp
op c1(): {@header foo: string, @statusCode _: 204};
op c1(): {@header foo: string, ...NoContent}; // or spread common model
```
22 changes: 22 additions & 0 deletions .chronus/changes/body-consitency-2024-2-27-18-35-44.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: breaking
packages:
- "@typespec/http"
---

`@body` means this is the body

This change makes it that using `@body` will mean exactly this is the body and everything underneath will be included, including metadata properties. It will log a warning explaining that.

```tsp
op a1(): {@body _: {@header foo: string, other: string} };
^ warning header in a body, it will not be included as a header.
```

Solution use `@bodyRoot` as the goal is only to change where to resolve the body from.

```tsp
op a1(): {@bodyRoot _: {@header foo: string, other: string} };
```

9 changes: 9 additions & 0 deletions .chronus/changes/body-consitency-2024-3-2-15-21-10.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: feature
packages:
- "@typespec/openapi3"
- "@typespec/rest"
---

Add supoort for new `@bodyRoot` and `@body` distinction
18 changes: 18 additions & 0 deletions .chronus/changes/body-consitency-2024-3-2-15-21-9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: breaking
packages:
- "@typespec/http"
---

Properties are not automatically omitted if everything was removed from metadata or visibility

```tsp
op d1(): {headers: {@header foo: string}}; // body will be {headers: {}}
```

Solution: use `@bodyIgnore`

```tsp
op d1(): {@bodyIgnore headers: {@header foo: string}}; // body will be {headers: {}}
```
68 changes: 67 additions & 1 deletion docs/libraries/http/reference/decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ toc_max_heading_level: 3

### `@body` {#@TypeSpec.Http.body}

Explicitly specify that this property is to be set as the body
Explicitly specify that this property type will be exactly the HTTP body.

This means that any properties under `@body` cannot be marked as headers, query parameters, or path parameters.
If wanting to change the resolution of the body but still mix parameters, use `@bodyRoot`.

```typespec
@TypeSpec.Http.body
Expand All @@ -33,6 +36,69 @@ op download(): {
};
```

### `@bodyIgnore` {#@TypeSpec.Http.bodyIgnore}

Specify that this property shouldn't be included in the HTTP body.
This can be useful when bundling metadata together that would result in an empty property to be included in the body.

```typespec
@TypeSpec.Http.bodyIgnore
```

#### Target

`ModelProperty`

#### Parameters

None

#### Examples

```typespec
op upload(
name: string,
@bodyIgnore headers: {
@header id: string;
},
): void;
```

### `@bodyRoot` {#@TypeSpec.Http.bodyRoot}

Specify that the body resolution should be resolved from that property.
By default the body is resolved by including all properties in the operation request/response that are not metadata.
This allows to nest the body in a property while still allowing to use headers, query parameters, and path parameters in the same model.

```typespec
@TypeSpec.Http.bodyRoot
```

#### Target

`ModelProperty`

#### Parameters

None

#### Examples

```typespec
op upload(
@bodyRoot user: {
name: string;
@header id: string;
},
): void;
op download(): {
@bodyRoot user: {
name: string;
@header id: string;
};
};
```

### `@delete` {#@TypeSpec.Http.delete}

Specify the HTTP verb for the target operation to be `DELETE`.
Expand Down
2 changes: 2 additions & 0 deletions docs/libraries/http/reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ npm install --save-peer @typespec/http
### Decorators

- [`@body`](./decorators.md#@TypeSpec.Http.body)
- [`@bodyIgnore`](./decorators.md#@TypeSpec.Http.bodyIgnore)
- [`@bodyRoot`](./decorators.md#@TypeSpec.Http.bodyRoot)
- [`@delete`](./decorators.md#@TypeSpec.Http.delete)
- [`@get`](./decorators.md#@TypeSpec.Http.get)
- [`@head`](./decorators.md#@TypeSpec.Http.head)
Expand Down
70 changes: 69 additions & 1 deletion packages/http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Available ruleSets:
### TypeSpec.Http

- [`@body`](#@body)
- [`@bodyIgnore`](#@bodyignore)
- [`@bodyRoot`](#@bodyroot)
- [`@delete`](#@delete)
- [`@get`](#@get)
- [`@head`](#@head)
Expand All @@ -55,7 +57,10 @@ Available ruleSets:

#### `@body`

Explicitly specify that this property is to be set as the body
Explicitly specify that this property type will be exactly the HTTP body.

This means that any properties under `@body` cannot be marked as headers, query parameters, or path parameters.
If wanting to change the resolution of the body but still mix parameters, use `@bodyRoot`.

```typespec
@TypeSpec.Http.body
Expand All @@ -78,6 +83,69 @@ op download(): {
};
```

#### `@bodyIgnore`

Specify that this property shouldn't be included in the HTTP body.
This can be useful when bundling metadata together that would result in an empty property to be included in the body.

```typespec
@TypeSpec.Http.bodyIgnore
```

##### Target

`ModelProperty`

##### Parameters

None

##### Examples

```typespec
op upload(
name: string,
@bodyIgnore headers: {
@header id: string;
},
): void;
```

#### `@bodyRoot`

Specify that the body resolution should be resolved from that property.
By default the body is resolved by including all properties in the operation request/response that are not metadata.
This allows to nest the body in a property while still allowing to use headers, query parameters, and path parameters in the same model.

```typespec
@TypeSpec.Http.bodyRoot
```

##### Target

`ModelProperty`

##### Parameters

None

##### Examples

```typespec
op upload(
@bodyRoot user: {
name: string;
@header id: string;
},
): void;
op download(): {
@bodyRoot user: {
name: string;
@header id: string;
};
};
```

#### `@delete`

Specify the HTTP verb for the target operation to be `DELETE`.
Expand Down
29 changes: 28 additions & 1 deletion packages/http/generated-defs/TypeSpec.Http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import type {
export type StatusCodeDecorator = (context: DecoratorContext, target: ModelProperty) => void;

/**
* Explicitly specify that this property is to be set as the body
* Explicitly specify that this property type will be exactly the HTTP body.
*
* This means that any properties under `@body` cannot be marked as headers, query parameters, or path parameters.
* If wanting to change the resolution of the body but still mix parameters, use `@bodyRoot`.
*
* @example
* ```typespec
Expand Down Expand Up @@ -84,6 +87,30 @@ export type PathDecorator = (
paramName?: string
) => void;

/**
* Specify that the body resolution should be resolved from that property.
* By default the body is resolved by including all properties in the operation request/response that are not metadata.
* This allows to nest the body in a property while still allowing to use headers, query parameters, and path parameters in the same model.
*
* @example
* ```typespec
* op upload(@bodyRoot user: {name: string, @header id: string}): void;
* op download(): {@bodyRoot user: {name: string, @header id: string}};
* ```
*/
export type BodyRootDecorator = (context: DecoratorContext, target: ModelProperty) => void;

/**
* Specify that this property shouldn't be included in the HTTP body.
* This can be useful when bundling metadata together that would result in an empty property to be included in the body.
*
* @example
* ```typespec
* op upload(name: string, @bodyIgnore headers: {@header id: string}): void;
* ```
*/
export type BodyIgnoreDecorator = (context: DecoratorContext, target: ModelProperty) => void;

/**
* Specify the HTTP verb for the target operation to be `GET`.
*
Expand Down
8 changes: 8 additions & 0 deletions packages/http/generated-defs/TypeSpec.Http.ts-test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/** An error here would mean that the decorator is not exported or doesn't have the right name. */
import {
$body,
$bodyIgnore,
$bodyRoot,
$delete,
$get,
$head,
Expand All @@ -19,6 +21,8 @@ import {
} from "@typespec/http";
import {
BodyDecorator,
BodyIgnoreDecorator,
BodyRootDecorator,
DeleteDecorator,
GetDecorator,
HeadDecorator,
Expand All @@ -42,6 +46,8 @@ type Decorators = {
$header: HeaderDecorator;
$query: QueryDecorator;
$path: PathDecorator;
$bodyRoot: BodyRootDecorator;
$bodyIgnore: BodyIgnoreDecorator;
$get: GetDecorator;
$put: PutDecorator;
$post: PostDecorator;
Expand All @@ -62,6 +68,8 @@ const _: Decorators = {
$header,
$query,
$path,
$bodyRoot,
$bodyIgnore,
$get,
$put,
$post,
Expand Down
30 changes: 29 additions & 1 deletion packages/http/lib/http-decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,10 @@ extern dec query(target: ModelProperty, queryNameOrOptions?: string | QueryOptio
extern dec path(target: ModelProperty, paramName?: valueof string);

/**
* Explicitly specify that this property is to be set as the body
* Explicitly specify that this property type will be exactly the HTTP body.
*
* This means that any properties under `@body` cannot be marked as headers, query parameters, or path parameters.
* If wanting to change the resolution of the body but still mix parameters, use `@bodyRoot`.
*
* @example
*
Expand All @@ -94,6 +97,31 @@ extern dec path(target: ModelProperty, paramName?: valueof string);
*/
extern dec body(target: ModelProperty);

/**
* Specify that the body resolution should be resolved from that property.
* By default the body is resolved by including all properties in the operation request/response that are not metadata.
* This allows to nest the body in a property while still allowing to use headers, query parameters, and path parameters in the same model.
*
* @example
*
* ```typespec
* op upload(@bodyRoot user: {name: string, @header id: string}): void;
* op download(): {@bodyRoot user: {name: string, @header id: string}};
* ```
*/
extern dec bodyRoot(target: ModelProperty);
/**
* Specify that this property shouldn't be included in the HTTP body.
* This can be useful when bundling metadata together that would result in an empty property to be included in the body.
*
* @example
*
* ```typespec
* op upload(name: string, @bodyIgnore headers: {@header id: string}): void;
* ```
*/
extern dec bodyIgnore(target: ModelProperty);

/**
* Specify the status code for this response. Property type must be a status code integer or a union of status code integer.
*
Expand Down
Loading
Loading