Support multi-service C# clients without @client decorator#10438
Support multi-service C# clients without @client decorator#10438jorgerangel-msft merged 8 commits intomainfrom
Conversation
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/10b8061a-9ce0-4c76-ab95-d30a73d2a6df Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…ant test comment Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/4bd1ac3b-75cd-410c-bd84-0700b628702e Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
commit: |
…for multi-service libraries Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/b533b9d9-348c-43f4-bd92-bb9ad5af287f Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
…ient to detect multi-root multi-service libraries Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/ef5611a0-3a47-4880-9b8d-695a47748e5a Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/eee2ac08-17bf-4c2a-881f-415a53f68b4e Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
|
@copilot there is a regression when regenerating our azure test libraries for this spec https://github.com/Azure/typespec-azure/blob/main/packages/azure-http-specs/specs/service/multi-service/client.tsp. The root namespace should be Service.MultiService.Combined. It is now truncating the Combined https://github.com/Azure/azure-sdk-for-net/blob/b20f4a432894f5f20685dc51ccf334f888572cec/eng/packages/http-client-csharp/generator/TestProjects/Spector/http/service/multi-service/src/Generated/Models/ServiceMultiServiceContext.cs. Also, the generated client options for service B is not correct https://github.com/Azure/azure-sdk-for-net/blob/b20f4a432894f5f20685dc51ccf334f888572cec/eng/packages/http-client-csharp/generator/TestProjects/Spector/http/service/multiple-services/src/Generated/ServiceBClientOptions.cs. It is using the api version enum from service A |
…ice options api version Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/2b0b711b-991b-4b7f-915c-f84ef3863da1 Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
Fixed both regressions in 3dd66c4.
All 1312 ClientModel generator tests and 201 emitter tests pass. |
Agent-Logs-Url: https://github.com/microsoft/typespec/sessions/9dfd093d-7997-4a3a-9d47-2231389307bf Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com>
When a TypeSpec library declares multiple
@service-decorated namespaces without an explicit@client(theservice/multiple-servicesspector scenario), TCGC emits one root client per service. The C# emitter's library-level logic didn't recognize this shape and resolved the package namespace to the first service's namespace instead of the shared parent, and the generator incorrectly shared a singleClientOptionsinstance across the per-service root clients.Emitter changes (
emitter/src/lib/utils.ts)getClientNamespaceString— when there are multiple@servicenamespaces (i.e.serviceNamespaces.length > 1) and the library does not contain a combined multi-service client, resolves the library namespace to the TypeSpec parentNamespaceshared by those services (viafirstNamespace.namespacefromlistAllServiceNamespaces) and passes it through the existing namespace-based helper path. ThecontainsMultiServiceClientcheck is evaluated first so that a combined@client({ service: [...] })under multiple nested@servicenamespaces (e.g. theservice/multi-servicespector spec) still resolves to the combined client's own namespace (e.g.Service.MultiService.Combined) rather than being truncated to the shared parent.containsMultiServiceClient— simplified torootClients.some(isMultiServiceClient)so it only returnstruewhen an individual client contains more than one service. The multi-root-multi-service case (multiple@servicenamespaces, each a separate single-service root) is handled directly ingetClientNamespaceStringvia theserviceNamespaces.length > 1check.For the
multiple-servicesspec, the library namespace now resolves toService.MultipleServicesinstead ofService.MultipleServices.ServiceA, withServiceAClientandServiceBClientas sibling root clients each exposing their ownOperations/SubNamespacesub-clients and per-service API version enums.Generator changes (
ClientOptionsProvider.cs)UseSingletonInstance— also returnsfalsewhen the input library contains more than oneApiVersionEnum(i.e. spans multiple services). Previously, when multiple per-service single-service root clients existed, both would share the singletonClientOptionsProvider, producing a single options class with only the first service's api version. Each root client now gets its ownClientOptionsclass (e.g.ServiceAClientOptions,ServiceBClientOptions).p.IsApiVersion && p.Type is InputEnumType), falling back to the library-wide enum list only when the client has no typed api-version parameter. Previously, the constructor always scanned all library enums and (for single-service clients) picked the first one, causingServiceBClientOptionsto nestServiceA's api version enum.Tests
emitter/test/Unit/client-converter.test.ts— newdescribe("multiple services without @client decorator", ...)block mirroring the spector scenario, asserting root count/names/namespaces, sub-client shape, per-serviceapiVersions, and thatcontainsMultiServiceClientreturnsfalse(since no individual root client is itself a combined multi-service client). Also adds a regression test for the combined@clientwith nested@servicenamespaces scenario (multi-servicespector spec) asserting the root namespace is the combined client's namespace (e.g.Service.MultiService.Combined).generator/.../ClientOptionsProviderTests.cs— newMultipleRootClients_WithoutClientDecorator_EachGeneratesItsOwnClientOptionstest verifying that two per-service root clients each produce their own distinctClientOptionstype with the expected names and that each options class nests its own service-specific api version enum (verified via the enum'sInputNamespace).