[typespec-ts] Empty resourceUri for tenant-scoped extension resources generates a double-slash URL
- Dominant language
- TypeScript
- Stars
- 27
- Forks
- 90
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 156
Description
## Describe the bug
The TypeScript emitter generates a URL template with a slash on both sides of the extension-resource scope parameter:
```ts
"/{+resourceUri}/providers/Azure.ResourceManager.Resources/extensionsResources/{extensionsResourceName}{?api%2Dversion}"
```
For tenant-scoped extension resources, `resourceUri` is correctly passed as an empty string. Expanding the template then produces a route beginning with two slashes:
```text
//providers/Azure.ResourceManager.Resources/extensionsResources/extension
```
The Spector server does not match this route and returns 404.
## Reproduction
Use the `azure/resource-manager/resources` Spector scenario and run:
```ts
await client.extensionsResources.get("", "extension");
```
The same problem affects all tenant-scoped extension-resource operations currently covered by `packages/typespec-ts/test/azure-modular-integration/azure-arm-resources.test.ts`:
- `get("", "extension")`
- `createOrUpdate("", "extension", resource)`
- `update("", "extension", resource)`
- `delete("", "extension")`
- `listByScope("")`
## Expected behavior
The request URL contains a single slash before `providers`:
```text
/providers/Azure.ResourceManager.Resources/extensionsResources/extension
```
An empty resource URI represents tenant scope. Callers should not need to pass `"."` or another placeholder to obtain the correct URL.
## Actual behavior
The generated requests contain `//providers/...` and receive 404 responses. For example:
```text
GET //providers/Azure.ResourceManager.Resources/extensionsResources/extension?api-version=2023-12-01-preview 404
```
The PUT, PATCH, DELETE, and list operations fail in the same way.
## Root cause
The generated `expandUrlTemplate` call expands this template:
```text
/{+resourceUri}/providers/...
```
With `resourceUri === ""`, the parameter contributes no characters while both literal slashes remain.
`@typespec/ts-http-runtime@0.3.7` intentionally preserves consecutive slashes inside a route path. Azure SDK for JS PR https://github.com/Azure/azure-sdk-for-js/pull/37302 removed the previous global slash normalization from `buildRequestUrl` and limits normalization to the endpoint/route boundary. Therefore, the runtime does not normalize this generated internal `//`.
## Suggested fix
Have the TypeScript emitter generate or expand the route so tenant scope produces `/providers/...` directly, without relying on runtime slash normalization. The fix should preserve the empty-string API for tenant scope and should not require the `"."` workaround.
## Environment
- `@azure-tools/typespec-ts`: 0.56.0
- `@typespec/ts-http-runtime`: 0.3.7
- `@azure-rest/core-client`: 2.8.0
- TypeSpec compiler: 1.14.0
Contributor guide
Research direction
Start with the azure/resource-manager/resources Spector scenario and packages/typespec-ts/test/azure-modular-integration/azure-arm-resources.test.ts, running the tenant-scoped operations with an empty resource URI. Trace the TypeScript emitter's generated expandUrlTemplate call and verify that get, createOrUpdate, update, delete, and listByScope produce a single slash before providers and no longer receive 404 responses.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100