Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
9c0ca78
Revert "[tcgc] one client multiple services (#3460)"
Dec 4, 2025
6fcd55f
wip
Dec 4, 2025
3f87b75
Merge branch 'main' of https://github.com/Azure/typespec-azure into t…
Dec 4, 2025
624da25
lint
Dec 4, 2025
356bc60
closer to passing tests
Dec 4, 2025
1284236
Merge https://github.com/Azure/typespec-azure into tcgc-multiple-service
Dec 5, 2025
1d920c0
versioning mutation not fully working
Dec 5, 2025
1ac3a67
tests passing
Dec 5, 2025
7b0ade8
add decorator
Dec 6, 2025
df25ca8
switch to multi service decoraotr
Dec 6, 2025
a2d4cec
Revert "switch to multi service decoraotr"
Dec 8, 2025
5946c5c
Revert "add decorator"
Dec 8, 2025
d3b150d
try new client design
Dec 8, 2025
79586df
most tests passing
Dec 8, 2025
e909e63
fix some versioning
Dec 8, 2025
24f8e9b
update logic
Dec 9, 2025
6ec8462
Merge branch 'main' of https://github.com/Azure/typespec-azure into t…
Dec 9, 2025
2811a69
Merge branch 'tcgc-multiple-service' of https://github.com/Azure/type…
Dec 9, 2025
12d867a
tests besides versioning working
Dec 9, 2025
8488f67
Merge branch 'main' of https://github.com/Azure/typespec-azure into t…
Dec 9, 2025
b8df586
fix return type of getPackageVersions so tests don't hang
Dec 9, 2025
afc95a5
change return types of functions
Dec 9, 2025
1400b13
Merge remote-tracking branch 'origin/main' into tcgc-multiple-service
Dec 10, 2025
8295613
remove automerge
Dec 10, 2025
2653bb3
update
Dec 10, 2025
c5f773f
Finish most logics for multiple services case
Dec 10, 2025
22e4c18
add tests
Dec 10, 2025
7dedcd9
add diagnostic tests
Dec 10, 2025
3937de8
add docs
Dec 10, 2025
1e85565
add changeset
Dec 10, 2025
738a592
add examples tests
Dec 10, 2025
e6ac719
support `@clientLocation` to new og for multiple services scenario
Dec 11, 2025
b57c80a
add test and fix realm
Dec 11, 2025
07610f5
auto apply version dependency
Dec 11, 2025
c338da9
fix example mapping problem and direct ops under service for multiple…
Dec 11, 2025
3cf322d
enhance robust
Dec 11, 2025
200d92f
fix multiple service client api version param
Dec 12, 2025
c020dcd
update docs
Dec 12, 2025
134d01a
update
Dec 12, 2025
611515b
update doc
Dec 15, 2025
b4d9cb5
add lint to validate endpoint and auth
Dec 15, 2025
8788e81
Update website/src/content/docs/docs/libraries/typespec-client-genera…
tadelesh Dec 16, 2025
ba43dfe
update docs
Dec 16, 2025
43ad8df
fix
Dec 17, 2025
ecc5332
update doc
Dec 17, 2025
206d497
fix mutation issue
Dec 17, 2025
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,7 @@
---
changeKind: feature
packages:
- "@azure-tools/typespec-client-generator-core"
---

Add support for a single client from multiple services
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# Multiple Service Support in TypeSpec Client Generator Core

## Background

Previously, TCGC [client](./client.md) only supported generating a client from a single service. However, in real-world scenarios, a single package often contains multiple services. TCGC must support multiple services within one package to address these needs.

## User Scenario

1. Merging multiple services' namespaces into one client

This scenario is common in Azure management services. For example, the compute team maintains several services: `Compute`, `Disk`, `Gallery`, and `Sku`. These services share the same endpoint and credential but have different versioning. When migrating these services into TypeSpec, services team wants to follow the existing way to generate SDK: geneate one SDK with a single client that could manage all these services with different versioning, instead of generate multiple SDKs for these multiple services. Therefore, TCGC must support auto-merging operations and nested namespaces/interfaces from multiple services with different versioning into one client.

For example, given two services `Disk` and `Gallery`, with Python SDK, the generated client code should look like:

```python
client = ComputeManagementClient(credential=DefaultAzureCredential(), subscription_id="{subscription-id}")
client.disks.list_by_resource_group(resource_group_name="myResourceGroup") # this operation will use API version defined in Disk service
client.galleries.list(location="eastus") # this operation will use API version defined in Gallery service
```

## First Step Design

The initial design focuses on the scenario of auto-merging multiple services into a single client. For other scenarios, such as redefining client hierarchy for multiple services, we will consider them in future iterations.

### Syntax Proposal

Previously, the `service` property of the `@client` option only supported a single service. We propose extending it to accept an array of services.

For example, given two services, the original service specs are:

```typespec title="ServiceA/main.tsp"
@service
@versioned(VersionsA)
namespace ServiceA;

enum VersionsA {
av1,
av2,
}
interface AI {
@route("/aTest")
aTest(@query("api-version") apiVersion: VersionsA): void;
}
```

```typespec title="ServiceB/main.tsp"
@service
@versioned(VersionsB)
namespace ServiceB;

enum VersionsB {
bv1,
bv2,
}
interface BI {
@route("/bTest")
bTest(@query("api-version") apiVersion: VersionsB): void;
}
```

To define a combined client:

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

using Azure.ClientGenerator.Core;

@client({
name: "CombineClient",
service: [ServiceA, ServiceB],
})
@useDependency(ServiceA.VersionsA.av2, ServiceB.VersionsB.bv2) // optional
namespace CombineClient;
```

**Explanation:**

- `@client` with the `service` property as an array indicates a combined client.
- `@useDependency` specifies the version for each service:
- If all services are unversioned, `@useDependency` can be omitted.
- If any service is versioned and `@useDependency` is not set, TCGC will use the latest version for each service by default.
- Only one `@client` with multiple services can be defined per package. Defining multiple such clients or mixing with `@operationGroup` is not supported.

### TCGC Behavior

When TCGC detects multiple services in one client, it will:

1. Create the root client for the combined client. If any service is versioned, the root client's initialization method will have an `apiVersion` parameter with no default value. The `apiVersions` property and the `apiVersion` parameter for the root client will be empty (since multiple services' API versions cannot be combined). The root client's endpoint and credential parameters will be created based on the first sub-service, which means all sub-services must share the same endpoint and credential.
2. Create sub-clients for each service's nested namespaces or interfaces. Each sub-client will have its own `apiVersion` property and initialization method if the service is versioned.
3. Operations directly under each service's namespace are placed under the root client. Operations under nested namespaces or interfaces are placed under the corresponding sub-clients.
4. Decorators such as `@clientLocation`, `@convenientAPI`, `@protocolAPI`, `@moveTo`, and `@scope` work as usual.
5. All other TCGC logic remains unchanged.
6. Since TCGC does not check if merging multiple services will cause sub-clients, models, operations, or other name conflicts, emitters must handle these conflicts appropriately.

For the example above, TCGC will generate types like:

```yaml
clients:
- &a1
kind: client
name: CombineClient
apiVersions: []
clientInitialization:
kind: clientinitialization
parameters:
- kind: endpoint
name: endpoint
isGeneratedName: true
onClient: true
- kind: method
name: apiVersion
apiVersions: []
clientDefaultValue: undefined
isGeneratedName: false
onClient: true
name: CombineClientOptions
isGeneratedName: true
initializedBy: individually
children:
- kind: client
name: AI
parent: *a1
apiVersions:
- av1
- av2
initialization:
kind: clientinitialization
parameters:
- kind: endpoint
name: endpoint
isGeneratedName: true
onClient: true
- kind: method
name: apiVersion
apiVersions:
- av1
- av2
clientDefaultValue: av2
isGeneratedName: false
onClient: true
name: AIOptions
isGeneratedName: true
initializedBy: parent
methods:
- kind: basic
name: aTest
- kind: client
name: BI
parent: *a1
apiVersions:
- bv1
- bv2
initialization:
kind: clientinitialization
parameters:
- kind: endpoint
name: endpoint
isGeneratedName: true
onClient: true
- kind: method
name: apiVersion
apiVersions:
- bv1
- bv2
clientDefaultValue: bv2
isGeneratedName: false
onClient: true
name: BIOptions
isGeneratedName: true
initializedBy: parent
methods:
- kind: basic
name: bTest
```
4 changes: 2 additions & 2 deletions packages/typespec-client-generator-core/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@ extern dec client(target: Namespace | Interface, options?: ClientOptions, scope?
*/
model ClientOptions {
/**
* The service that this client is generated for. If not specified, TCGC will look up the first parent namespace decorated with `@service` for the target.
* The services that this client is generated for. If not specified, TCGC will look up the first parent namespace decorated with `@service` for the target.
* The namespace should be decorated with `@service`.
*/
service?: Namespace;
service?: Namespace | Namespace[];

/**
* The name of the client. If not specified, the default name will be `<Name of the target>Client`.
Expand Down
Loading
Loading