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
Expand Up @@ -27,3 +27,4 @@ Within the azure-rest-api-specs repository, the TypeSpec specification serves as
- Read up on [Folder Structure for TypeSpec](./typespec-structure-guidelines.md)
- [Setting up a new TypeSpec project](./typespec-rest-api-dev-process.md#3-creating-a-new-typespec-project)
- [Setting up and regenerate SDK projects](./typespec-rest-api-dev-process.md#5-generate-sdk-code-from-a-typespec-project)
- [Body Parameter Naming Conventions for PATCH and POST operations](./body-parameter-naming-guide.md)
250 changes: 250 additions & 0 deletions documentation/body-parameter-naming-approach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# Relationship: TypeSpec Decorators vs. BodyParameterNameNormalizer

## Background

This document explains the relationship between:
1. The `BodyParameterNameNormalizer.cs` file mentioned in issue [Azure/azure-sdk-for-net#XXXXX](https://github.com/Azure/azure-sdk-for-net/blob/main/eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Utilities/BodyParameterNameNormalizer.cs)
2. The TypeSpec `@@clientName` decorator solution documented in this repository

## The Issue

The issue states:
> The current logic only handles suffix-based naming and does not consider the operation type. The expected behavior should be:
> 1. **For PATCH operations**: If the request body is a model, the parameter name should be `patch`.
> 2. **For POST operations**: If the request body is a model, the parameter name should be `content`.

## Two Approaches to Solve This

### Approach 1: Update Code Generator (azure-sdk-for-net)

**Location**: `Azure/azure-sdk-for-net` repository
**File**: `eng/packages/http-client-csharp-mgmt/generator/Azure.Generator.Management/src/Utilities/BodyParameterNameNormalizer.cs`

**What needs to change:**
```csharp
// Current implementation (simplified)
public static string GetNormalizedName(Parameter parameter)
{
// Only handles suffix-based naming
if (parameter.Name.EndsWith("Parameters"))
return RemoveSuffix(parameter.Name);
return parameter.Name;
}

// Proposed enhancement
public static string GetNormalizedName(Parameter parameter, HttpMethod method)
{
// Consider operation type
if (method == HttpMethod.Patch && parameter.IsBodyParameter)
return "patch";
if (method == HttpMethod.Post && parameter.IsBodyParameter)
return "content";

// Fallback to suffix-based naming
if (parameter.Name.EndsWith("Parameters"))
return RemoveSuffix(parameter.Name);
return parameter.Name;
}
```

**Pros:**
- Automatic naming for all operations
- No changes needed in TypeSpec files
- Consistent behavior by default

**Cons:**
- Less control for spec authors
- Hard-coded rules in generator
- Difficult to override for special cases

### Approach 2: Use TypeSpec @@clientName Decorators (Recommended)

**Location**: `Azure/azure-rest-api-specs` repository
**File**: `client.tsp` in each service specification

**Implementation:**
```typespec
import "./main.tsp";
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core;

namespace YourService {
// PATCH operations
@@clientName(Resources.update::parameters.properties, "patch", "csharp");

// POST operations
@@clientName(Resources.action::parameters.body, "content", "csharp");
}
```

**Pros:**
- ✅ Explicit and clear intent
- ✅ Works across all language generators
- ✅ Spec authors have full control
- ✅ Can override for special cases
- ✅ Already supported and widely used
- ✅ No generator changes needed

**Cons:**
- Requires adding decorators to each service
- Manual work for existing services

## Recommended Solution

**Use TypeSpec `@@clientName` decorators** (Approach 2) for the following reasons:

### 1. TypeSpec-Native Solution
The `@@clientName` decorator is the TypeSpec-native way to customize generated code. It's designed exactly for this use case.

### 2. Already Implemented and Working
Many Azure services already use this pattern successfully:
- Service Fabric Managed Clusters
- Storage Management
- Health Data AI Services
- And many more...

### 3. Cross-Language Support
```typespec
// Can specify different names for different languages
@@clientName(Resources.update::parameters.properties, "patch", "csharp,java,python");
```

### 4. Flexibility
```typespec
// Can override for special cases
@@clientName(Resources.specialUpdate::parameters.properties, "updateData", "csharp");
```

### 5. No Breaking Changes
Since this is opt-in (you add decorators where needed), it doesn't break existing code.

## Implementation Guide

### For New TypeSpec Services

When creating a new TypeSpec service, add a `client.tsp` file:

```typespec
import "./main.tsp";
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core;

namespace YourService {
// Add @@clientName decorators for all PATCH and POST operations
@@clientName(Resources.update::parameters.properties, "patch", "csharp");
@@clientName(Resources.action::parameters.body, "content", "csharp");
}
```

### For Existing TypeSpec Services

1. Create a `client.tsp` file if it doesn't exist
2. Identify all PATCH and POST operations
3. Add appropriate `@@clientName` decorators
4. Test the generated SDK to verify parameter names

### For Services Still Using Swagger/OpenAPI

If your service hasn't migrated to TypeSpec yet, you can:
1. Wait for TypeSpec migration
2. Or manually adjust parameter names in the OpenAPI definition (not recommended)

## Validation

To verify your decorators are working:

1. **Compile the TypeSpec:**
```bash
tsp compile .
```

2. **Generate the SDK:**
```bash
# Generate C# SDK
tsp-client generate --language csharp
```

3. **Check the generated code:**
Look for methods with PATCH/POST operations and verify parameter names are "patch" or "content".

## Examples

### Before (no decorators):
```csharp
public Response<Widget> Update(
string resourceGroupName,
string widgetName,
WidgetUpdateParameters widgetUpdateParameters, // ❌ Long, redundant
CancellationToken cancellationToken = default)
```

### After (with decorators):
```csharp
public Response<Widget> Update(
string resourceGroupName,
string widgetName,
WidgetUpdateParameters patch, // ✅ Clear, concise
CancellationToken cancellationToken = default)
```

## FAQ

### Q: Why not just update the code generator?

**A:** While updating the code generator is possible, using TypeSpec decorators is:
- More flexible
- Language-agnostic
- Already supported
- Doesn't require generator changes
- Gives spec authors control

### Q: Do I need to add decorators for PUT operations?

**A:** Usually no. PUT operations typically create full resources, and the parameter name naturally reflects the resource type. However, you can add decorators if desired for consistency.

### Q: What about other HTTP methods (DELETE, GET)?

**A:** GET and DELETE typically don't have request bodies. If you have a special case, you can use `@@clientName` to customize any parameter name.

### Q: Can I use different names for different languages?

**A:** Yes! Example:
```typespec
@@clientName(Resources.update::parameters.properties, "patch", "csharp,java");
@@clientName(Resources.update::parameters.properties, "update_data", "python");
```

### Q: What if I have multiple PATCH or POST operations?

**A:** Add a decorator for each one:
```typespec
@@clientName(Resources.update::parameters.properties, "patch", "csharp");
@@clientName(SubResources.update::parameters.properties, "patch", "csharp");
@@clientName(Resources.action1::parameters.body, "content", "csharp");
@@clientName(Resources.action2::parameters.body, "content", "csharp");
```

## Conclusion

While the issue mentions updating `BodyParameterNameNormalizer.cs` in the azure-sdk-for-net repository, the **recommended solution is to use TypeSpec `@@clientName` decorators** in the azure-rest-api-specs repository. This approach is:

- ✅ More maintainable
- ✅ More flexible
- ✅ Already proven to work
- ✅ Requires no generator changes
- ✅ Works across all languages

## Related Documentation

- [Body Parameter Naming Guide](./body-parameter-naming-guide.md) - Complete implementation guide
- [Body Parameter Naming Examples](./body-parameter-naming-examples.md) - Before/after comparisons
- [TypeSpec Client Generator Core](https://github.com/Azure/typespec-azure/tree/main/packages/typespec-client-generator-core)

## Next Steps

1. Review the [Body Parameter Naming Guide](./body-parameter-naming-guide.md)
2. Implement `@@clientName` decorators in your service's `client.tsp`
3. Test the generated SDK
4. Submit a PR with the changes
Loading