Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: internal
packages:
- "@azure-tools/azure-http-specs"
---

Remove use of implicit optionality which was a noop in one operation
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
changeKind: breaking
packages:
- "@azure-tools/typespec-azure-resource-manager"
---

Removed `implicitOptionality: true` from the `@patch` decorator on the following operation templates:

- `ArmTagsPatchAsync`
- `ArmResourcePatchAsync`
- `ArmTagsPatchSync`
- `ArmResourcePatchSync`
- `ResourceUpdateSync.update` (in `ResourceUpdateSync` interface)

Previously, these templates used `@patch(#{ implicitOptionality: true })` which caused all properties in the PATCH request body to be implicitly treated as optional, regardless of how they were declared in the model. This behavior is now removed — properties will retain their declared optionality.

#### Migration

If you use these ARM operation templates, the PATCH body will no longer implicitly make all properties optional. In most cases, ARM resources already use `ResourceUpdateModel<Resource, Properties>` which produces the correct optional property envelope — if so, **no changes are needed**.

If you have a custom patch body that relied on implicit optionality, explicitly mark properties as optional:

**Before** (implicit optionality made all properties optional automatically):

```tsp
model MyResourceProperties {
displayName: string;
config: MyConfig;
}
```

**After** (explicitly declare optional properties for update):

```tsp
model MyResourceUpdateProperties {
displayName?: string;
config?: MyConfig;
}
```

Or continue using `ResourceUpdateModel<Resource, Properties>` which already handles this transformation for standard ARM resource patterns.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
changeKind: breaking
packages:
- "@azure-tools/typespec-azure-core"
---

Removed `implicitOptionality: true` from the `@patch` decorator on the following operation templates:

- `ResourceCreateOrUpdate`
- `LongRunningResourceCreateOrUpdate`
- `ResourceUpdate`

Previously, these templates used `@patch(#{ implicitOptionality: true })` which caused all properties in the PATCH request body to be implicitly treated as optional, regardless of how they were declared in the model. This behavior is now removed — properties will retain their declared optionality.

#### Migration

If your service relies on all properties being optional in the PATCH body, you need to explicitly mark them as optional in your model:

**Before** (implicit optionality made all properties optional automatically):

```tsp
model Widget {
name: string;
color: string;
}
```

**After** (explicitly declare optional properties):

```tsp
model WidgetUpdate {
name?: string;
color?: string;
}
```

Alternatively, you can use the `ResourceUpdateModel` template or `OptionalProperties` utility to derive an all-optional version of your model for PATCH operations. If you were already using `ResourceUpdateModel<Resource>` or manually defining optional properties in your update model, no changes are needed.
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ model UpdateWidgetColorRequest {
```
""")
@doc("Update widget color (preview only)")
@patch(#{ implicitOptionality: true })
@patch
@route("/widgets/{id}/color")
@added(ApiVersions.v2024_12_01_preview)
op updateWidgetColor(
Expand Down
11 changes: 3 additions & 8 deletions packages/typespec-azure-core/lib/operations.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,10 @@ interface ResourceOperations<
* @template Resource Resource type.
* @template Traits Object describing the traits of the operation.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@Foundations.Private.ensureVerb("ResourceCreateOrUpdate", "PATCH")
@createsOrUpdatesResource(Resource)
@parameterVisibility(Lifecycle.Create, Lifecycle.Update)
@patch(#{ implicitOptionality: true }) // For legacy reasons
@patch
ResourceCreateOrUpdate<
Resource extends TypeSpec.Reflection.Model,
Traits extends TypeSpec.Reflection.Model = {}
Expand Down Expand Up @@ -193,12 +191,10 @@ interface ResourceOperations<
* @template Resource Resource type.
* @template Traits Object describing the traits of the operation.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@Foundations.Private.ensureVerb("LongRunningResourceCreateOrUpdate", "PATCH")
@createsOrUpdatesResource(Resource)
@parameterVisibility(Lifecycle.Create, Lifecycle.Update)
@patch(#{ implicitOptionality: true }) // For legacy reasons
@patch
LongRunningResourceCreateOrUpdate<
Resource extends TypeSpec.Reflection.Model,
Traits extends TypeSpec.Reflection.Model = {}
Expand Down Expand Up @@ -232,10 +228,9 @@ interface ResourceOperations<
* @template Resource Resource type.
* @template Traits Object describing the traits of the operation.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@Foundations.Private.ensureVerb("ResourceUpdate", "PATCH")
@updatesResource(Resource)
@patch(#{ implicitOptionality: true }) // For legacy reasons
@patch
ResourceUpdate<
Resource extends TypeSpec.Reflection.Model,
Traits extends TypeSpec.Reflection.Model = {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ op CreateOrReplaceAsync<
@extensionResourceOperation(TargetResource, ExtensionResource, "update", OverrideResourceName)
@enforceConstraint(TargetResource, Foundations.Resource)
@enforceConstraint(ExtensionResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchAsync<
TargetResource extends Foundations.SimpleResource,
ExtensionResource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -271,7 +271,7 @@ op CustomPatchAsync<
@extensionResourceOperation(TargetResource, ExtensionResource, "update", OverrideResourceName)
@enforceConstraint(TargetResource, Foundations.Resource)
@enforceConstraint(ExtensionResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchSync<
TargetResource extends Foundations.SimpleResource,
ExtensionResource extends Foundations.SimpleResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,7 @@ interface ResourceUpdateSync<
* @template Properties RP-specific property bag for the resource
* @template BaseParameters The http parameters that are part of the request
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@patch(#{ implicitOptionality: true })
@patch
update is ArmCustomPatchSync<Resource, ResourceUpdateModel<Resource, Properties>, BaseParameters>;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ op CreateOrReplaceSync<
@extensionResourceOperation(TargetResource, ExtensionResource, "update", OverrideResourceName)
@enforceConstraint(TargetResource, Foundations.Resource)
@enforceConstraint(ExtensionResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchAsync<
TargetResource extends Foundations.SimpleResource,
ExtensionResource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -193,7 +193,7 @@ op CustomPatchAsync<
@extensionResourceOperation(TargetResource, ExtensionResource, "update", OverrideResourceName)
@enforceConstraint(TargetResource, Foundations.Resource)
@enforceConstraint(ExtensionResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchSync<
TargetResource extends Foundations.SimpleResource,
ExtensionResource extends Foundations.SimpleResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ interface ExtensionOperations<
@armOperationRoute(OperationOptions)
@doc("Update a {name}", Resource)
@legacyExtensionResourceOperation(Resource, "update", OverrideResourceName)
@patch(#{ implicitOptionality: false })
@patch
CustomPatchAsync<
Resource extends Foundations.SimpleResource,
PatchModel extends {} | void = Azure.ResourceManager.Foundations.TagsUpdateModel<Resource>,
Expand Down Expand Up @@ -146,7 +146,7 @@ interface ExtensionOperations<
@armOperationRoute(OperationOptions)
@doc("Update a {name}", Resource)
@legacyExtensionResourceOperation(Resource, "update", OverrideResourceName)
@patch(#{ implicitOptionality: false })
@patch
CustomPatchSync<
Resource extends Foundations.SimpleResource,
PatchModel extends {} | void = Azure.ResourceManager.Foundations.TagsUpdateModel<Resource>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ interface RoutedOperations<
@armOperationRoute(OverrideRouteOptions)
@legacyResourceOperation(Resource, "update", OverrideResourceName)
@Private.armUpdateProviderNamespace
@patch(#{ implicitOptionality: false })
@patch
CustomPatchAsync<
Resource extends Foundations.SimpleResource,
PatchModel extends {} | void = Azure.ResourceManager.Foundations.TagsUpdateModel<Resource>,
Expand Down Expand Up @@ -175,7 +175,7 @@ interface RoutedOperations<
@armOperationRoute(OverrideRouteOptions)
@legacyResourceOperation(Resource, "update", OverrideResourceName)
@Private.armUpdateProviderNamespace
@patch(#{ implicitOptionality: false })
@patch
CustomPatchSync<
Resource extends Foundations.SimpleResource,
PatchModel extends {} | void = Azure.ResourceManager.Foundations.TagsUpdateModel<Resource>,
Expand Down Expand Up @@ -464,7 +464,7 @@ model ProviderParameter<Resource extends {}> {
@doc("Update a {name}", Resource)
@armResourceUpdate(Resource)
@Private.enforceConstraint(Resource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchAsync<
Resource extends Foundations.SimpleResource,
PatchModel extends TypeSpec.Reflection.Model | void = TagsUpdateModel<Resource>,
Expand Down Expand Up @@ -503,7 +503,7 @@ op CustomPatchAsync<
@doc("Update a {name}", Resource)
@armResourceUpdate(Resource)
@Private.enforceConstraint(Resource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchSync<
Resource extends Foundations.SimpleResource,
PatchModel extends TypeSpec.Reflection.Model | void = TagsUpdateModel<Resource>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ op CreateOrReplaceAsync<
@doc("Update a {name} PrivateEndpointConnection", ParentResource)
@armResourceUpdate(Resource)
@Private.enforceConstraint(ParentResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchAsync<
ParentResource extends Foundations.SimpleResource,
Resource extends PrivateEndpointConnectionResource,
Expand Down Expand Up @@ -175,7 +175,7 @@ op CustomPatchAsync<
@doc("Update a {name} PrivateEndpointConnection", ParentResource)
@armResourceUpdate(Resource)
@Private.enforceConstraint(ParentResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
op CustomPatchSync<
ParentResource extends Foundations.SimpleResource,
Resource extends PrivateEndpointConnectionResource,
Expand Down
16 changes: 6 additions & 10 deletions packages/typespec-azure-resource-manager/lib/operations.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,7 @@ op ArmResourceCreateOrReplaceAsync<
* @template Parameters Optional. Additional parameters after the path parameters
* @template Provider Optional. The provider namespace model for the resource.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@patch(#{ implicitOptionality: true })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmTagsPatchAsync<
Resource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -377,8 +376,7 @@ op ArmTagsPatchAsync<
* @template Parameters Optional. Additional parameters after the path parameters
* @template Provider Optional. The provider namespace model for the resource.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@patch(#{ implicitOptionality: true })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmResourcePatchAsync<
Resource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -417,7 +415,7 @@ op ArmResourcePatchAsync<
@autoRoute
@doc("Update a {name}", Resource)
@armResourceUpdate(Resource)
@patch(#{ implicitOptionality: false })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmCustomPatchAsync<
Resource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -449,8 +447,7 @@ op ArmCustomPatchAsync<
* @template Parameters Optional. Additional parameters after the path parameters
* @template Provider Optional. The provider namespace model for the resource.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@patch(#{ implicitOptionality: true })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmTagsPatchSync<
Resource extends Foundations.SimpleResource,
Expand All @@ -475,8 +472,7 @@ op ArmTagsPatchSync<
* @template Parameters Optional. Additional parameters after the path parameters
* @template Provider Optional. The provider namespace model for the resource.
*/
#suppress "@typespec/http/deprecated-implicit-optionality" "Legacy"
@patch(#{ implicitOptionality: true })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmResourcePatchSync<
Resource extends Foundations.SimpleResource,
Expand Down Expand Up @@ -507,7 +503,7 @@ op ArmResourcePatchSync<
@autoRoute
@doc("Update a {name}", Resource)
@armResourceUpdate(Resource)
@patch(#{ implicitOptionality: false })
@patch
@Private.enforceConstraint(Resource, Foundations.Resource)
op ArmCustomPatchSync<
Resource extends Foundations.SimpleResource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ interface PrivateEndpoints<
@doc("Update a {name} PrivateEndpointConnection", ParentResource)
@builtInResourceOperation(ParentResource, Resource, "update", OverrideResourceName)
@Private.enforceConstraint(ParentResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
CustomPatchAsync<
ParentResource extends Foundations.SimpleResource,
Resource extends PrivateEndpointConnectionResource = PrivateEndpointResource,
Expand Down Expand Up @@ -286,7 +286,7 @@ interface PrivateEndpoints<
@doc("Update a {name PrivateEndpointConnection}", ParentResource)
@builtInResourceOperation(ParentResource, Resource, "update", OverrideResourceName)
@Private.enforceConstraint(ParentResource, Foundations.Resource)
@patch(#{ implicitOptionality: false })
@patch
CustomPatchSync<
ParentResource extends Foundations.SimpleResource,
Resource extends PrivateEndpointConnectionResource = PrivateEndpointResource,
Expand Down
Loading