diff --git a/.chronus/changes/ReleaseDashboardsStep1-2024-11-5-14-40-39.md b/.chronus/changes/ReleaseDashboardsStep1-2024-11-5-14-40-39.md
new file mode 100644
index 0000000000..e3e74262ca
--- /dev/null
+++ b/.chronus/changes/ReleaseDashboardsStep1-2024-11-5-14-40-39.md
@@ -0,0 +1,8 @@
+---
+changeKind: internal
+packages:
+ - "@typespec/http-specs"
+ - "@typespec/spector"
+---
+
+Adding scripts to package.json
\ No newline at end of file
diff --git a/.chronus/changes/nullable-2024-11-6-1-23-27.md b/.chronus/changes/nullable-2024-11-6-1-23-27.md
new file mode 100644
index 0000000000..5aa6b8945c
--- /dev/null
+++ b/.chronus/changes/nullable-2024-11-6-1-23-27.md
@@ -0,0 +1,8 @@
+---
+# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
+changeKind: fix
+packages:
+ - "@typespec/http-server-csharp"
+---
+
+Fix nullable types, anonymous types, and safeInt
diff --git a/.chronus/changes/synced-with-versioning-removed-in-cadl-ranch-2024-10-28-15-49-29.md b/.chronus/changes/synced-with-versioning-removed-in-cadl-ranch-2024-10-28-15-49-29.md
new file mode 100644
index 0000000000..3704e00677
--- /dev/null
+++ b/.chronus/changes/synced-with-versioning-removed-in-cadl-ranch-2024-10-28-15-49-29.md
@@ -0,0 +1,7 @@
+---
+changeKind: fix
+packages:
+ - "@typespec/http-specs"
+---
+
+update code in versioning/removed and removed type/model/templated.
diff --git a/eng/common/config/labels.ts b/eng/common/config/labels.ts
index cb9c284ec0..ce4ec6b35d 100644
--- a/eng/common/config/labels.ts
+++ b/eng/common/config/labels.ts
@@ -194,6 +194,10 @@ export default defineConfig({
misc: {
description: "Misc labels",
labels: {
+ "1_0_E2E": {
+ color: "5319E7",
+ description: "",
+ },
"Client Emitter Migration": {
color: "FD92F0",
description: "",
diff --git a/package.json b/package.json
index 786d4a21ec..adb2207254 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,11 @@
"test:e2e": "pnpm -r run test:e2e",
"update-latest-docs": "pnpm -r run update-latest-docs",
"watch": "tsc --build ./tsconfig.ws.json --watch",
- "sync-labels": "tsx ./eng/common/scripts/labels/sync-labels.ts --config ./eng/common/config/labels.ts"
+ "sync-labels": "tsx ./eng/common/scripts/labels/sync-labels.ts --config ./eng/common/config/labels.ts",
+ "validate-scenarios": "pnpm -r --filter=@typespec/http-specs run validate-scenarios",
+ "validate-mock-apis": "pnpm -r --filter=@typespec/http-specs run validate-mock-apis",
+ "generate-scenarios-summary": "pnpm -r --filter=@typespec/http-specs run generate-scenarios-summary",
+ "upload-manifest": "pnpm -r --filter=@typespec/http-specs run upload-manifest"
},
"devDependencies": {
"@chronus/chronus": "^0.13.0",
diff --git a/packages/http-client-csharp/eng/scripts/Generate.ps1 b/packages/http-client-csharp/eng/scripts/Generate.ps1
index ee44432ff6..05134142c8 100644
--- a/packages/http-client-csharp/eng/scripts/Generate.ps1
+++ b/packages/http-client-csharp/eng/scripts/Generate.ps1
@@ -101,6 +101,14 @@ foreach ($directory in $directories) {
continue
}
+ # srv-driven contains two separate specs, for two separate clients. We need to generate both.
+ if ($folders.Contains("srv-driven")) {
+ Generate-Srv-Driven $directory.FullName $generationDir -generateStub $stubbed
+ $cadlRanchLaunchProjects.Add($($folders -join "-") + "-v1", $("TestProjects/CadlRanch/$($subPath.Replace([System.IO.Path]::DirectorySeparatorChar, '/'))") + "/v1")
+ $cadlRanchLaunchProjects.Add($($folders -join "-") + "-v2", $("TestProjects/CadlRanch/$($subPath.Replace([System.IO.Path]::DirectorySeparatorChar, '/'))") + "/v2")
+ continue
+ }
+
$cadlRanchLaunchProjects.Add(($folders -join "-"), ("TestProjects/CadlRanch/$($subPath.Replace([System.IO.Path]::DirectorySeparatorChar, '/'))"))
if ($LaunchOnly) {
continue
@@ -114,11 +122,6 @@ foreach ($directory in $directories) {
exit $LASTEXITCODE
}
- # srv-driven contains two separate specs, for two separate clients. We need to generate both.
- if ($folders.Contains("srv-driven")) {
- Generate-Srv-Driven $directory.FullName $generationDir -generateStub $stubbed
- }
-
# TODO need to build but depends on https://github.com/Azure/autorest.csharp/issues/4463
}
diff --git a/packages/http-client-csharp/eng/scripts/Generation.psm1 b/packages/http-client-csharp/eng/scripts/Generation.psm1
index a2c6af5274..cd10f88dc3 100644
--- a/packages/http-client-csharp/eng/scripts/Generation.psm1
+++ b/packages/http-client-csharp/eng/scripts/Generation.psm1
@@ -91,14 +91,27 @@ function Generate-Srv-Driven {
[bool]$createOutputDirIfNotExist = $true
)
- $specFilePath = $(Join-Path $specFilePath "old.tsp")
- $outputDir = $(Join-Path $outputDir "v1")
- if ($createOutputDirIfNotExist -and -not (Test-Path $outputDir)) {
- New-Item -ItemType Directory -Path $outputDir | Out-Null
+ $v1Dir = $(Join-Path $outputDir "v1")
+ if ($createOutputDirIfNotExist -and -not (Test-Path $v1Dir)) {
+ New-Item -ItemType Directory -Path $v1Dir | Out-Null
}
- Write-Host "Generating http\resiliency\srv-driven\v1" -ForegroundColor Cyan
- Invoke (Get-TspCommand $specFilePath $outputDir -generateStub $generateStub -namespaceOverride "Resiliency.ServiceDriven.V1")
+ $v2Dir = $(Join-Path $outputDir "v2")
+ if ($createOutputDirIfNotExist -and -not (Test-Path $v2Dir)) {
+ New-Item -ItemType Directory -Path $v2Dir | Out-Null
+ }
+
+ ## get the last two directories of the output directory and add V1/V2 to disambiguate the namespaces
+ $namespaceRoot = $(($outputDir.Split([System.IO.Path]::DirectorySeparatorChar)[-2..-1] | `
+ ForEach-Object { $_.Substring(0,1).ToUpper() + $_.Substring(1) }) -replace '-(\p{L})', { $_.Groups[1].Value.ToUpper() } -replace '\W', '' -join ".")
+ $v1NamespaceOverride = $namespaceRoot + ".V1"
+ $v2NamespaceOverride = $namespaceRoot + ".V2"
+
+ $v1SpecFilePath = $(Join-Path $specFilePath "old.tsp")
+ $v2SpecFilePath = $(Join-Path $specFilePath "main.tsp")
+
+ Invoke (Get-TspCommand $v1SpecFilePath $v1Dir -generateStub $generateStub -namespaceOverride $v1NamespaceOverride)
+ Invoke (Get-TspCommand $v2SpecFilePath $v2Dir -generateStub $generateStub -namespaceOverride $v2NamespaceOverride)
# exit if the generation failed
if ($LASTEXITCODE -ne 0) {
diff --git a/packages/http-client-csharp/eng/scripts/Get-CadlRanch-Coverage.ps1 b/packages/http-client-csharp/eng/scripts/Get-CadlRanch-Coverage.ps1
index da844ced98..ff668da5aa 100644
--- a/packages/http-client-csharp/eng/scripts/Get-CadlRanch-Coverage.ps1
+++ b/packages/http-client-csharp/eng/scripts/Get-CadlRanch-Coverage.ps1
@@ -46,17 +46,20 @@ foreach ($directory in $directories) {
continue
}
+ if ($subPath.Contains("srv-driven")) {
+ if ($subPath.Contains("v1")) {
+ # this will generate v1 and v2 so we only need to call it once for one of the versions
+ Generate-Srv-Driven ($(Join-Path $specsDirectory $subPath) | Split-Path) $($outputDir | Split-Path) -createOutputDirIfNotExist $false
+ }
+ continue
+ }
+
$command = Get-TspCommand $specFile $outputDir
Invoke $command
# exit if the generation failed
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
-
- # srv-driven contains two separate specs, for two separate clients. We need to generate both.
- if ($subPath.Contains('srv-driven')) {
- Generate-Srv-Driven $(Join-Path $specsDirectory $subPath) $outputDir -createOutputDirIfNotExist $false
- }
}
# test all
diff --git a/packages/http-client-csharp/eng/scripts/Test-CadlRanch.ps1 b/packages/http-client-csharp/eng/scripts/Test-CadlRanch.ps1
index 65266e5ac8..698c0537df 100644
--- a/packages/http-client-csharp/eng/scripts/Test-CadlRanch.ps1
+++ b/packages/http-client-csharp/eng/scripts/Test-CadlRanch.ps1
@@ -32,10 +32,6 @@ foreach ($directory in $directories) {
if (-not (Compare-Paths $subPath $filter)) {
continue
}
-
- if ($subPath.Contains($(Join-Path 'srv-driven' 'v1'))) {
- continue
- }
$testPath = "$cadlRanchRoot.Tests"
$testFilter = "TestProjects.CadlRanch.Tests"
@@ -67,21 +63,21 @@ foreach ($directory in $directories) {
Generate-Versioning ($(Join-Path $specsDirectory $subPath) | Split-Path) $($outputDir | Split-Path) -createOutputDirIfNotExist $false
}
}
+ elseif ($subPath.Contains("srv-driven")) {
+ if ($subPath.Contains("v1")) {
+ Generate-Srv-Driven ($(Join-Path $specsDirectory $subPath) | Split-Path) $($outputDir | Split-Path) -createOutputDirIfNotExist $false
+ }
+ }
else {
$command = Get-TspCommand $specFile $outputDir
Invoke $command
}
-
+
# exit if the generation failed
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
- # srv-driven contains two separate specs, for two separate clients. We need to generate both.
- if ($subPath.Contains("srv-driven")) {
- Generate-Srv-Driven $(Join-Path $specsDirectory $subPath) $outputDir -createOutputDirIfNotExist $false
- }
-
Write-Host "Testing $subPath" -ForegroundColor Cyan
$command = "dotnet test $cadlRanchCsproj --filter `"FullyQualifiedName~$testFilter`""
Invoke $command
diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json
index a8e2843798..f2d5ebb72d 100644
--- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json
+++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Properties/launchSettings.json
@@ -120,8 +120,13 @@
"commandName": "Executable",
"executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
},
- "http-resiliency-srv-driven": {
- "commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/resiliency/srv-driven -p StubLibraryPlugin",
+ "http-resiliency-srv-driven-v1": {
+ "commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/resiliency/srv-driven/v1 -p StubLibraryPlugin",
+ "commandName": "Executable",
+ "executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
+ },
+ "http-resiliency-srv-driven-v2": {
+ "commandLineArgs": "$(SolutionDir)/TestProjects/CadlRanch/http/resiliency/srv-driven/v2 -p StubLibraryPlugin",
"commandName": "Executable",
"executablePath": "$(SolutionDir)/../dist/generator/Microsoft.Generator.CSharp.exe"
},
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.V1.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V1/SrvDrivenV1Tests.cs
similarity index 94%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.V1.cs
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V1/SrvDrivenV1Tests.cs
index 02f91bb8ba..dd321081b3 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.V1.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V1/SrvDrivenV1Tests.cs
@@ -2,16 +2,19 @@
// Licensed under the MIT License.
using NUnit.Framework;
-using Resiliency.ServiceDriven.V1;
+using Resiliency.SrvDriven.V1;
using System.Threading.Tasks;
-namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven
+namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven.V1
{
///
/// Contains tests for the service-driven resiliency V1 client.
///
- public partial class SrvDrivenTests : CadlRanchTestBase
+ public partial class SrvDrivenV2Tests : CadlRanchTestBase
{
+ private const string ServiceDeploymentV1 = "v1";
+ private const string ServiceDeploymentV2 = "v2";
+
// This test validates the v1 client behavior when both the service deployment and api version are set to V1.
[CadlRanchTest]
public Task AddOptionalParamFromNone_V1Client_V1Service_WithApiVersionV1() => Test(async (host) =>
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V2/SrvDrivenV2Tests.cs
similarity index 96%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.cs
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V2/SrvDrivenV2Tests.cs
index ca6a493224..cc2dfb9985 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/SrvDrivenTests.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Resiliency/SrvDriven/V2/SrvDrivenV2Tests.cs
@@ -3,13 +3,12 @@
using NUnit.Framework;
using System.Threading.Tasks;
-using Resiliency.ServiceDriven;
+using Resiliency.SrvDriven.V2;
-namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven
+namespace TestProjects.CadlRanch.Tests.Http.Resiliency.SrvDriven.V2
{
- public partial class SrvDrivenTests : CadlRanchTestBase
+ public partial class SrvDrivenV2Tests : CadlRanchTestBase
{
- private const string ServiceDeploymentV1 = "v1";
private const string ServiceDeploymentV2 = "v2";
[CadlRanchTest]
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Versioning/Removed/V1/VersioningRemovedV1Tests.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Versioning/Removed/V1/VersioningRemovedV1Tests.cs
index 3fdf09cc9e..d96e2a4292 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Versioning/Removed/V1/VersioningRemovedV1Tests.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Http/Versioning/Removed/V1/VersioningRemovedV1Tests.cs
@@ -3,8 +3,10 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using NUnit.Framework;
using Versioning.Removed.V1;
+using Versioning.Removed.V1.Models;
namespace TestProjects.CadlRanch.Tests.Http.Versioning.Removed.V1
{
@@ -39,5 +41,15 @@ public void TestRemovedMembers()
var enumType = typeof(RemovedClientOptions.ServiceVersion);
Assert.AreEqual(new string[] { "V1" }, enumType.GetEnumNames());
}
+
+ [CadlRanchTest]
+ public Task Versioning_Removed_V3Model() => Test(async (host) =>
+ {
+ var model = new ModelV3("123", EnumV3.EnumMemberV1);
+ var response = await new RemovedClient(host).ModelV3Async(model);
+ Assert.AreEqual(200, response.GetRawResponse().Status);
+ Assert.AreEqual("123", response.Value.Id);
+ Assert.AreEqual(EnumV3.EnumMemberV1, response.Value.EnumProp);
+ });
}
}
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Infrastructure/CadlRanchTestAttribute.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Infrastructure/CadlRanchTestAttribute.cs
index 32688b3275..29897ff980 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Infrastructure/CadlRanchTestAttribute.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch.Tests/Infrastructure/CadlRanchTestAttribute.cs
@@ -24,6 +24,12 @@ internal partial class CadlRanchTestAttribute : TestAttribute, IApplyToTest
{
string clientCodeDirectory = GetGeneratedDirectory(test);
+ if (!Directory.Exists(clientCodeDirectory))
+ {
+ // Not all cadl-ranch scenarios use kebab-case directories, so try again without kebab-case.
+ clientCodeDirectory = GetGeneratedDirectory(test, false);
+ }
+
var clientCsFile = GetClientCsFile(clientCodeDirectory);
TestContext.Progress.WriteLine($"Checking if '{clientCsFile}' is a stubbed implementation.");
@@ -69,21 +75,26 @@ private static void SkipTest(Test test)
.FirstOrDefault();
}
- private static string GetGeneratedDirectory(Test test)
+ private static string GetGeneratedDirectory(Test test, bool kebabCaseDirectories = true)
{
var namespaceParts = test.FullName.Split('.').Skip(3);
namespaceParts = namespaceParts.Take(namespaceParts.Count() - 2);
var clientCodeDirectory = Path.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", "..", "..", "TestProjects", "CadlRanch");
foreach (var part in namespaceParts)
{
- clientCodeDirectory = Path.Combine(clientCodeDirectory, FixName(part));
+ clientCodeDirectory = Path.Combine(clientCodeDirectory, FixName(part, kebabCaseDirectories));
}
return Path.Combine(clientCodeDirectory, "src", "Generated");
}
- private static string FixName(string part)
+ private static string FixName(string part, bool kebabCaseDirectories)
{
- return ToKebabCase().Replace(part.StartsWith("_", StringComparison.Ordinal) ? part.Substring(1) : part, "-$1").ToLower();
+ if (kebabCaseDirectories)
+ {
+ return ToKebabCase().Replace(part.StartsWith("_", StringComparison.Ordinal) ? part.Substring(1) : part, "-$1").ToLowerInvariant();
+ }
+ // Use camelCase
+ return char.ToLowerInvariant(part[0]) + part[1..];
}
}
}
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Configuration.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Configuration.json
deleted file mode 100644
index 03f7a6232d..0000000000
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Configuration.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "output-folder": ".",
- "namespace": "Resiliency.ServiceDriven",
- "library-name": "Resiliency.ServiceDriven",
- "use-model-reader-writer": true
-}
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Configuration.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Configuration.json
index 23dcd7aba7..43d151251e 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Configuration.json
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Configuration.json
@@ -1,6 +1,6 @@
{
"output-folder": ".",
- "namespace": "Resiliency.ServiceDriven.V1",
- "library-name": "Resiliency.ServiceDriven.V1",
+ "namespace": "Resiliency.SrvDriven.V1",
+ "library-name": "Resiliency.SrvDriven.V1",
"use-model-reader-writer": true
}
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Resiliency.ServiceDriven.sln b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.SrvDriven.V1.sln
similarity index 96%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Resiliency.ServiceDriven.sln
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.SrvDriven.V1.sln
index 1022c33783..92889ce27d 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/Resiliency.ServiceDriven.sln
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.SrvDriven.V1.sln
@@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resiliency.ServiceDriven", "src\Resiliency.ServiceDriven.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resiliency.SrvDriven.V1", "src\Resiliency.SrvDriven.V1.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClient.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClient.cs
index 857cc0ef51..8c25a81940 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClient.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClient.cs
@@ -8,7 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Resiliency.ServiceDriven.V1
+namespace Resiliency.SrvDriven.V1
{
public partial class ResiliencyServiceDrivenClient
{
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClientOptions.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClientOptions.cs
index 9c387842e9..adf5be0b0f 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClientOptions.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Generated/ResiliencyServiceDrivenClientOptions.cs
@@ -4,7 +4,7 @@
using System.ClientModel.Primitives;
-namespace Resiliency.ServiceDriven.V1
+namespace Resiliency.SrvDriven.V1
{
public partial class ResiliencyServiceDrivenClientOptions : ClientPipelineOptions
{
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Resiliency.ServiceDriven.csproj b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.SrvDriven.V1.csproj
similarity index 62%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Resiliency.ServiceDriven.csproj
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.SrvDriven.V1.csproj
index a8a84fc716..5f5ff2e5e4 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Resiliency.ServiceDriven.csproj
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.SrvDriven.V1.csproj
@@ -1,9 +1,9 @@
- This is the Resiliency.ServiceDriven client library for developing .NET applications with rich experience.
- SDK Code Generation Resiliency.ServiceDriven
+ This is the Resiliency.SrvDriven.V1 client library for developing .NET applications with rich experience.
+ SDK Code Generation Resiliency.SrvDriven.V1
1.0.0-beta.1
- Resiliency.ServiceDriven
+ Resiliency.SrvDriven.V1
netstandard2.0
latest
true
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Configuration.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Configuration.json
new file mode 100644
index 0000000000..b479794aef
--- /dev/null
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Configuration.json
@@ -0,0 +1,6 @@
+{
+ "output-folder": ".",
+ "namespace": "Resiliency.SrvDriven.V2",
+ "library-name": "Resiliency.SrvDriven.V2",
+ "use-model-reader-writer": true
+}
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.ServiceDriven.V1.sln b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Resiliency.SrvDriven.V2.sln
similarity index 96%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.ServiceDriven.V1.sln
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Resiliency.SrvDriven.V2.sln
index 2507d0b1ce..47ec60440c 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/Resiliency.ServiceDriven.V1.sln
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/Resiliency.SrvDriven.V2.sln
@@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resiliency.ServiceDriven.V1", "src\Resiliency.ServiceDriven.V1.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Resiliency.SrvDriven.V2", "src\Resiliency.SrvDriven.V2.csproj", "{28FF4005-4467-4E36-92E7-DEA27DEB1519}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClient.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClient.cs
similarity index 98%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClient.cs
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClient.cs
index ab38b9c6be..6cc3562d37 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClient.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClient.cs
@@ -8,7 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
-namespace Resiliency.ServiceDriven
+namespace Resiliency.SrvDriven.V2
{
public partial class ResiliencyServiceDrivenClient
{
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClientOptions.cs b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClientOptions.cs
similarity index 94%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClientOptions.cs
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClientOptions.cs
index 233945b82e..11ababcb63 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/src/Generated/ResiliencyServiceDrivenClientOptions.cs
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Generated/ResiliencyServiceDrivenClientOptions.cs
@@ -4,7 +4,7 @@
using System.ClientModel.Primitives;
-namespace Resiliency.ServiceDriven
+namespace Resiliency.SrvDriven.V2
{
public partial class ResiliencyServiceDrivenClientOptions : ClientPipelineOptions
{
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.ServiceDriven.V1.csproj b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Resiliency.SrvDriven.V2.csproj
similarity index 61%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.ServiceDriven.V1.csproj
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Resiliency.SrvDriven.V2.csproj
index fa7663cb74..0cf65023a6 100644
--- a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v1/src/Resiliency.ServiceDriven.V1.csproj
+++ b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/src/Resiliency.SrvDriven.V2.csproj
@@ -1,9 +1,9 @@
- This is the Resiliency.ServiceDriven.V1 client library for developing .NET applications with rich experience.
- SDK Code Generation Resiliency.ServiceDriven.V1
+ This is the Resiliency.SrvDriven.V2 client library for developing .NET applications with rich experience.
+ SDK Code Generation Resiliency.SrvDriven.V2
1.0.0-beta.1
- Resiliency.ServiceDriven.V1
+ Resiliency.SrvDriven.V2
netstandard2.0
latest
true
diff --git a/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/tspCodeModel.json b/packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/tspCodeModel.json
similarity index 100%
rename from packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/tspCodeModel.json
rename to packages/http-client-csharp/generator/TestProjects/CadlRanch/http/resiliency/srv-driven/v2/tspCodeModel.json
diff --git a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/EnumTemplate.java b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/EnumTemplate.java
index d7fbac8ba4..f75dd15152 100644
--- a/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/EnumTemplate.java
+++ b/packages/http-client-java/generator/http-client-generator-core/src/main/java/com/microsoft/typespec/http/client/generator/core/template/EnumTemplate.java
@@ -67,6 +67,7 @@ protected void writeBrandedExpandableEnum(EnumType enumType, JavaFile javaFile,
imports.add("java.util.ArrayList");
imports.add("java.util.Objects");
imports.add(ClassType.EXPANDABLE_ENUM.getFullName());
+ imports.add("java.util.function.Function");
if (!settings.isStreamStyleSerialization()) {
imports.add("com.fasterxml.jackson.annotation.JsonCreator");
}
@@ -84,6 +85,8 @@ protected void writeBrandedExpandableEnum(EnumType enumType, JavaFile javaFile,
javaFile.publicFinalClass(declaration, classBlock -> {
classBlock.privateStaticFinalVariable(
String.format("Map<%1$s, %2$s> VALUES = new ConcurrentHashMap<>()", pascalTypeName, enumName));
+ classBlock.privateStaticFinalVariable(
+ String.format("Function<%1$s, %2$s> NEW_INSTANCE = %2$s::new", pascalTypeName, enumName));
for (ClientEnumValue enumValue : enumType.getValues()) {
String value = enumValue.getValue();
@@ -115,9 +118,7 @@ protected void writeBrandedExpandableEnum(EnumType enumType, JavaFile javaFile,
classBlock.publicStaticMethod(String.format("%1$s fromValue(%2$s value)", enumName, pascalTypeName),
function -> {
function.line("Objects.requireNonNull(value, \"'value' cannot be null.\");");
- function.line(enumName + " member = VALUES.get(value);");
- function.ifBlock("member != null", ifAction -> ifAction.line("return member;"));
- function.methodReturn("VALUES.computeIfAbsent(value, key -> new " + enumName + "(key))");
+ function.methodReturn("VALUES.computeIfAbsent(value, NEW_INSTANCE)");
});
// values
@@ -150,7 +151,7 @@ protected void writeBrandedExpandableEnum(EnumType enumType, JavaFile javaFile,
addGeneratedAnnotation(classBlock);
classBlock.annotation("Override");
classBlock.method(JavaVisibility.Public, null, "boolean equals(Object obj)",
- function -> function.methodReturn("Objects.equals(this.value, obj)"));
+ function -> function.methodReturn("this == obj"));
// hashcode
addGeneratedAnnotation(classBlock);
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/armresourceprovider/models/PriorityModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/armresourceprovider/models/PriorityModel.java
index 80db9412da..33536bff25 100644
--- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/armresourceprovider/models/PriorityModel.java
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/armresourceprovider/models/PriorityModel.java
@@ -11,6 +11,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
/**
* Defines values for PriorityModel.
@@ -18,6 +19,8 @@
public final class PriorityModel implements ExpandableEnum {
private static final Map VALUES = new ConcurrentHashMap<>();
+ private static final Function NEW_INSTANCE = PriorityModel::new;
+
/**
* Static value 0 for PriorityModel.
*/
@@ -43,11 +46,7 @@ private PriorityModel(Integer value) {
@JsonCreator
public static PriorityModel fromValue(Integer value) {
Objects.requireNonNull(value, "'value' cannot be null.");
- PriorityModel member = VALUES.get(value);
- if (member != null) {
- return member;
- }
- return VALUES.computeIfAbsent(value, key -> new PriorityModel(key));
+ return VALUES.computeIfAbsent(value, NEW_INSTANCE);
}
/**
@@ -76,7 +75,7 @@ public String toString() {
@Override
public boolean equals(Object obj) {
- return Objects.equals(this.value, obj);
+ return this == obj;
}
@Override
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/OlympicRecordModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/OlympicRecordModel.java
index dce6221287..c46f716d2b 100644
--- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/OlympicRecordModel.java
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/OlympicRecordModel.java
@@ -11,6 +11,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
/**
* Defines values for OlympicRecordModel.
@@ -18,6 +19,8 @@
public final class OlympicRecordModel implements ExpandableEnum {
private static final Map VALUES = new ConcurrentHashMap<>();
+ private static final Function NEW_INSTANCE = OlympicRecordModel::new;
+
/**
* Static value 9.58 for OlympicRecordModel.
*/
@@ -45,11 +48,7 @@ private OlympicRecordModel(Double value) {
@Generated
public static OlympicRecordModel fromValue(Double value) {
Objects.requireNonNull(value, "'value' cannot be null.");
- OlympicRecordModel member = VALUES.get(value);
- if (member != null) {
- return member;
- }
- return VALUES.computeIfAbsent(value, key -> new OlympicRecordModel(key));
+ return VALUES.computeIfAbsent(value, NEW_INSTANCE);
}
/**
@@ -82,7 +81,7 @@ public String toString() {
@Generated
@Override
public boolean equals(Object obj) {
- return Objects.equals(this.value, obj);
+ return this == obj;
}
@Generated
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/PriorityModel.java b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/PriorityModel.java
index 99432a3cf0..43b1221128 100644
--- a/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/PriorityModel.java
+++ b/packages/http-client-java/generator/http-client-generator-test/src/main/java/tsptest/enumservice/models/PriorityModel.java
@@ -11,6 +11,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
/**
* Defines values for PriorityModel.
@@ -18,6 +19,8 @@
public final class PriorityModel implements ExpandableEnum {
private static final Map VALUES = new ConcurrentHashMap<>();
+ private static final Function NEW_INSTANCE = PriorityModel::new;
+
/**
* Static value 100 for PriorityModel.
*/
@@ -45,11 +48,7 @@ private PriorityModel(Integer value) {
@Generated
public static PriorityModel fromValue(Integer value) {
Objects.requireNonNull(value, "'value' cannot be null.");
- PriorityModel member = VALUES.get(value);
- if (member != null) {
- return member;
- }
- return VALUES.computeIfAbsent(value, key -> new PriorityModel(key));
+ return VALUES.computeIfAbsent(value, NEW_INSTANCE);
}
/**
@@ -82,7 +81,7 @@ public String toString() {
@Generated
@Override
public boolean equals(Object obj) {
- return Objects.equals(this.value, obj);
+ return this == obj;
}
@Generated
diff --git a/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/enumservice/EnumTests.java b/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/enumservice/EnumTests.java
index 05bf2aa4df..fd3ba7b708 100644
--- a/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/enumservice/EnumTests.java
+++ b/packages/http-client-java/generator/http-client-generator-test/src/test/java/tsptest/enumservice/EnumTests.java
@@ -23,6 +23,7 @@
import tsptest.enumservice.implementation.EnumServiceClientImpl;
import tsptest.enumservice.models.ColorModel;
import tsptest.enumservice.models.Priority;
+import tsptest.enumservice.models.PriorityModel;
public class EnumTests {
@@ -129,6 +130,15 @@ public void testStringArrayAsMulti() throws Exception {
Assertions.assertEquals("colorArrayOpt=Green&colorArrayOpt=Red", request.getUrl().getQuery());
}
+ @Test
+ public void testExpandableEnum() {
+ Assertions.assertEquals(PriorityModel.HIGH, PriorityModel.fromValue(100));
+ Assertions.assertNotEquals(PriorityModel.HIGH, PriorityModel.LOW);
+ Assertions.assertNotEquals(PriorityModel.HIGH, PriorityModel.fromValue(200));
+
+ Assertions.assertEquals(100, PriorityModel.HIGH.getValue());
+ }
+
private static void verifyQuery(String query, String key, String value) {
Assertions.assertEquals(
URLEncoder.encode(key, StandardCharsets.UTF_8) + "=" + URLEncoder.encode(value, StandardCharsets.UTF_8),
diff --git a/packages/http-client-python/CHANGELOG.md b/packages/http-client-python/CHANGELOG.md
index 8248785288..b143c2cff5 100644
--- a/packages/http-client-python/CHANGELOG.md
+++ b/packages/http-client-python/CHANGELOG.md
@@ -1,5 +1,21 @@
# Change Log - @typespec/http-client-python
+## 0.4.1
+
+### Bug Fixes
+
+- Ignore models only used as LRO envelope results because we don't do anything with them
+
+## 0.4.0
+
+### Features
+
+- Refine exception handling logic and support exception with ranged status code (#5270)
+
+### Bug Fixes
+
+- Filter out credential that python does not support for now (#5282)
+
## 0.3.12
### Other Changes
diff --git a/packages/http-client-python/emitter/src/code-model.ts b/packages/http-client-python/emitter/src/code-model.ts
index 0199c7de21..3afb945f30 100644
--- a/packages/http-client-python/emitter/src/code-model.ts
+++ b/packages/http-client-python/emitter/src/code-model.ts
@@ -269,6 +269,9 @@ export function emitCodeModel(
}
// loop through models and enums since there may be some orphaned models needs to be generated
for (const model of sdkPackage.models) {
+ if (isAzureCoreModel(model)) {
+ continue;
+ }
// filter out spread models
if (
model.name === "" ||
@@ -278,6 +281,16 @@ export function emitCodeModel(
) {
continue;
}
+ // filter out models only used for polling and or envelope result
+ if (
+ ((model.usage & UsageFlags.LroInitial) > 0 ||
+ (model.usage & UsageFlags.LroFinalEnvelope) > 0 ||
+ (model.usage & UsageFlags.LroPolling) > 0) &&
+ (model.usage & UsageFlags.Input) === 0 &&
+ (model.usage & UsageFlags.Output) === 0
+ ) {
+ continue;
+ }
// filter out specific models not used in python, e.g., pageable models
if (disableGenerationMap.has(model)) {
continue;
@@ -289,6 +302,9 @@ export function emitCodeModel(
getType(sdkContext, model);
}
for (const sdkEnum of sdkPackage.enums) {
+ if (isAzureCoreModel(sdkEnum)) {
+ continue;
+ }
// filter out api version enum since python do not generate it
if (sdkEnum.usage === UsageFlags.ApiVersionEnum) {
continue;
diff --git a/packages/http-client-python/emitter/src/http.ts b/packages/http-client-python/emitter/src/http.ts
index ad62dd4082..cd2ae43904 100644
--- a/packages/http-client-python/emitter/src/http.ts
+++ b/packages/http-client-python/emitter/src/http.ts
@@ -379,7 +379,7 @@ function emitHttpResponse(
headers: response.headers.map((x) => emitHttpResponseHeader(context, x)),
statusCodes:
typeof statusCodes === "object"
- ? [(statusCodes as HttpStatusCodeRange).start]
+ ? [[(statusCodes as HttpStatusCodeRange).start, (statusCodes as HttpStatusCodeRange).end]]
: statusCodes === "*"
? ["default"]
: [statusCodes],
diff --git a/packages/http-client-python/generator/pygen/codegen/__init__.py b/packages/http-client-python/generator/pygen/codegen/__init__.py
index 11d829263c..1ab9bd6237 100644
--- a/packages/http-client-python/generator/pygen/codegen/__init__.py
+++ b/packages/http-client-python/generator/pygen/codegen/__init__.py
@@ -241,6 +241,16 @@ def _validate_code_model_options(self) -> None:
if not self.options_retriever.is_azure_flavor and self.options_retriever.tracing:
raise ValueError("Can only have tracing turned on for Azure SDKs.")
+ @staticmethod
+ def sort_exceptions(yaml_data: Dict[str, Any]) -> None:
+ for client in yaml_data["clients"]:
+ for group in client["operationGroups"]:
+ for operation in group["operations"]:
+ if not operation.get("exceptions"):
+ continue
+ # sort exceptions by status code, first single status code, then range, then default
+ operation["exceptions"] = sorted(operation["exceptions"], key=lambda x: 3 if x["statusCodes"][0] == "default" else (1 if isinstance(x["statusCodes"][0], int) else 2))
+
@staticmethod
def remove_cloud_errors(yaml_data: Dict[str, Any]) -> None:
for client in yaml_data["clients"]:
@@ -315,6 +325,8 @@ def process(self) -> bool:
self._validate_code_model_options()
options = self._build_code_model_options()
yaml_data = self.get_yaml()
+
+ self.sort_exceptions(yaml_data)
if self.options_retriever.azure_arm:
self.remove_cloud_errors(yaml_data)
diff --git a/packages/http-client-python/generator/pygen/codegen/models/model_type.py b/packages/http-client-python/generator/pygen/codegen/models/model_type.py
index 80e21252b4..09422a44e3 100644
--- a/packages/http-client-python/generator/pygen/codegen/models/model_type.py
+++ b/packages/http-client-python/generator/pygen/codegen/models/model_type.py
@@ -348,7 +348,7 @@ def serialization_type(self) -> str:
@property
def instance_check_template(self) -> str:
- return "isinstance({}, _model_base.Model)"
+ return "isinstance({}, " + f"_models.{self.name})"
def imports(self, **kwargs: Any) -> FileImport:
file_import = super().imports(**kwargs)
diff --git a/packages/http-client-python/generator/pygen/codegen/models/operation.py b/packages/http-client-python/generator/pygen/codegen/models/operation.py
index 06631ca907..0ce0c62aee 100644
--- a/packages/http-client-python/generator/pygen/codegen/models/operation.py
+++ b/packages/http-client-python/generator/pygen/codegen/models/operation.py
@@ -9,6 +9,7 @@
List,
Any,
Optional,
+ Tuple,
Union,
TYPE_CHECKING,
Generic,
@@ -201,17 +202,11 @@ def default_error_deserialization(self) -> Optional[str]:
exception_schema = default_exceptions[0].type
if isinstance(exception_schema, ModelType):
return exception_schema.type_annotation(skip_quote=True)
- # in this case, it's just an AnyType
- return "'object'"
+ return None
@property
def non_default_errors(self) -> List[Response]:
- return [e for e in self.exceptions if "default" not in e.status_codes]
-
- @property
- def non_default_error_status_codes(self) -> List[Union[str, int]]:
- """Actually returns all of the status codes from exceptions (besides default)"""
- return list(chain.from_iterable([error.status_codes for error in self.non_default_errors]))
+ return [e for e in self.exceptions if "default" not in e.status_codes and e.type and isinstance(e.type, ModelType)]
def _imports_shared(self, async_mode: bool, **kwargs: Any) -> FileImport: # pylint: disable=unused-argument
file_import = FileImport(self.code_model)
@@ -344,19 +339,7 @@ def imports( # pylint: disable=too-many-branches, disable=too-many-statements
file_import.add_submodule_import("exceptions", error, ImportType.SDKCORE)
if self.code_model.options["azure_arm"]:
file_import.add_submodule_import("azure.mgmt.core.exceptions", "ARMErrorFormat", ImportType.SDKCORE)
- if self.non_default_errors:
- file_import.add_submodule_import(
- "typing",
- "Type",
- ImportType.STDLIB,
- )
file_import.add_mutable_mapping_import()
- if self.non_default_error_status_codes:
- file_import.add_submodule_import(
- "typing",
- "cast",
- ImportType.STDLIB,
- )
if self.has_kwargs_to_pop_with_default(
self.parameters.kwargs_to_pop, ParameterLocation.HEADER # type: ignore
@@ -436,7 +419,7 @@ def imports( # pylint: disable=too-many-branches, disable=too-many-statements
elif any(r.type for r in self.responses):
file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
if self.default_error_deserialization or self.non_default_errors:
- file_import.add_submodule_import(f"{relative_path}_model_base", "_deserialize", ImportType.LOCAL)
+ file_import.add_submodule_import(f"{relative_path}_model_base", "_failsafe_deserialize", ImportType.LOCAL)
return file_import
def get_response_from_status(self, status_code: Optional[Union[str, int]]) -> ResponseType:
diff --git a/packages/http-client-python/generator/pygen/codegen/models/response.py b/packages/http-client-python/generator/pygen/codegen/models/response.py
index 19a7d62e94..c36a98a371 100644
--- a/packages/http-client-python/generator/pygen/codegen/models/response.py
+++ b/packages/http-client-python/generator/pygen/codegen/models/response.py
@@ -54,7 +54,7 @@ def __init__(
type: Optional[BaseType] = None,
) -> None:
super().__init__(yaml_data=yaml_data, code_model=code_model)
- self.status_codes: List[Union[int, str]] = yaml_data["statusCodes"]
+ self.status_codes: List[Union[int, str, List[int]]] = yaml_data["statusCodes"]
self.headers = headers or []
self.type = type
self.nullable = yaml_data.get("nullable")
diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py
index de7d6c2086..34924397e1 100644
--- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py
+++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py
@@ -61,14 +61,6 @@ def _all_same(data: List[List[str]]) -> bool:
return len(data) > 1 and all(sorted(data[0]) == sorted(data[i]) for i in range(1, len(data)))
-def _need_type_ignore(builder: OperationType) -> bool:
- for e in builder.non_default_errors:
- for status_code in e.status_codes:
- if status_code in (401, 404, 409, 304):
- return True
- return False
-
-
def _xml_config(send_xml: bool, content_types: List[str]) -> str:
if not (send_xml and "xml" in str(content_types)):
return ""
@@ -999,20 +991,80 @@ def handle_error_response(self, builder: OperationType) -> List[str]:
elif isinstance(builder.stream_value, str): # _stream is not sure, so we need to judge it
retval.append(" if _stream:")
retval.extend([f" {l}" for l in response_read])
- type_ignore = " # type: ignore" if _need_type_ignore(builder) else ""
retval.append(
- f" map_error(status_code=response.status_code, response=response, error_map=error_map){type_ignore}"
+ f" map_error(status_code=response.status_code, response=response, error_map=error_map)"
)
error_model = ""
+ if builder.non_default_errors and self.code_model.options["models_mode"]:
+ error_model = ", model=error"
+ condition = "if"
+ retval.append(" error = None")
+ for e in builder.non_default_errors:
+ # single status code
+ if isinstance(e.status_codes[0], int):
+ for status_code in e.status_codes:
+ retval.append(f" {condition} response.status_code == {status_code}:")
+ if self.code_model.options["models_mode"] == "dpg":
+ retval.append(f" error = _failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, response.json())")
+ else:
+ retval.append(
+ f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, "
+ "pipeline_response)"
+ )
+ # add build-in error type
+ # TODO: we should decide whether need to this wrapper for customized error type
+ if status_code == 401:
+ retval.append(
+ " raise ClientAuthenticationError(response=response{}{})".format(
+ error_model,
+ (", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
+ )
+ )
+ elif status_code == 404:
+ retval.append(
+ " raise ResourceNotFoundError(response=response{}{})".format(
+ error_model,
+ (", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
+ )
+ )
+ elif status_code == 409:
+ retval.append(
+ " raise ResourceExistsError(response=response{}{})".format(
+ error_model,
+ (", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
+ )
+ )
+ elif status_code == 304:
+ retval.append(
+ " raise ResourceNotModifiedError(response=response{}{})".format(
+ error_model,
+ (", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""),
+ )
+ )
+ # ranged status code only exist in typespec and will not have multiple status codes
+ else:
+ retval.append(f" {condition} {e.status_codes[0][0]} <= response.status_code <= {e.status_codes[0][1]}:")
+ if self.code_model.options["models_mode"] == "dpg":
+ retval.append(f" error = _failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, response.json())")
+ else:
+ retval.append(
+ f" error = self._deserialize.failsafe_deserialize({e.type.type_annotation(is_operation_file=True, skip_quote=True)}, "
+ "pipeline_response)"
+ )
+ condition = "elif"
+ # default error handling
if builder.default_error_deserialization and self.code_model.options["models_mode"]:
+ error_model = ", model=error"
+ indent = " " if builder.non_default_errors else " "
+ if builder.non_default_errors:
+ retval.append(" else:")
if self.code_model.options["models_mode"] == "dpg":
- retval.append(f" error = _deserialize({builder.default_error_deserialization}, response.json())")
+ retval.append(f"{indent}error = _failsafe_deserialize({builder.default_error_deserialization}, response.json())")
else:
retval.append(
- f" error = self._deserialize.failsafe_deserialize({builder.default_error_deserialization}, "
+ f"{indent}error = self._deserialize.failsafe_deserialize({builder.default_error_deserialization}, "
"pipeline_response)"
)
- error_model = ", model=error"
retval.append(
" raise HttpResponseError(response=response{}{})".format(
error_model,
@@ -1085,60 +1137,28 @@ def handle_response(self, builder: OperationType) -> List[str]:
retval.append("return 200 <= response.status_code <= 299")
return retval
+ def _need_specific_error_map(self, code: int, builder: OperationType) -> bool:
+ for non_default_error in builder.non_default_errors:
+ # single status code
+ if code in non_default_error.status_codes:
+ return False
+ # ranged status code
+ if isinstance(non_default_error.status_codes[0], list) and non_default_error.status_codes[0][0] <= code <= non_default_error.status_codes[0][1]:
+ return False
+ return True
+
def error_map(self, builder: OperationType) -> List[str]:
retval = ["error_map: MutableMapping = {"]
- if builder.non_default_errors:
- if not 401 in builder.non_default_error_status_codes:
+ if builder.non_default_errors and self.code_model.options["models_mode"]:
+ # TODO: we should decide whether to add the build-in error map when there is a customized default error type
+ if self._need_specific_error_map(401, builder):
retval.append(" 401: ClientAuthenticationError,")
- if not 404 in builder.non_default_error_status_codes:
+ if self._need_specific_error_map(404, builder):
retval.append(" 404: ResourceNotFoundError,")
- if not 409 in builder.non_default_error_status_codes:
+ if self._need_specific_error_map(409, builder):
retval.append(" 409: ResourceExistsError,")
- if not 304 in builder.non_default_error_status_codes:
+ if self._need_specific_error_map(304, builder):
retval.append(" 304: ResourceNotModifiedError,")
- for e in builder.non_default_errors:
- error_model_str = ""
- if isinstance(e.type, ModelType):
- if self.code_model.options["models_mode"] == "msrest":
- error_model_str = (
- f", model=self._deserialize(" f"_models.{e.type.serialization_type}, response)"
- )
- elif self.code_model.options["models_mode"] == "dpg":
- error_model_str = f", model=_deserialize(_models.{e.type.name}, response.json())"
- error_format_str = ", error_format=ARMErrorFormat" if self.code_model.options["azure_arm"] else ""
- for status_code in e.status_codes:
- if status_code == 401:
- retval.append(
- " 401: cast(Type[HttpResponseError], "
- "lambda response: ClientAuthenticationError(response=response"
- f"{error_model_str}{error_format_str})),"
- )
- elif status_code == 404:
- retval.append(
- " 404: cast(Type[HttpResponseError], "
- "lambda response: ResourceNotFoundError(response=response"
- f"{error_model_str}{error_format_str})),"
- )
- elif status_code == 409:
- retval.append(
- " 409: cast(Type[HttpResponseError], "
- "lambda response: ResourceExistsError(response=response"
- f"{error_model_str}{error_format_str})),"
- )
- elif status_code == 304:
- retval.append(
- " 304: cast(Type[HttpResponseError], "
- "lambda response: ResourceNotModifiedError(response=response"
- f"{error_model_str}{error_format_str})),"
- )
- elif not error_model_str and not error_format_str:
- retval.append(f" {status_code}: HttpResponseError,")
- else:
- retval.append(
- f" {status_code}: cast(Type[HttpResponseError], "
- "lambda response: HttpResponseError(response=response"
- f"{error_model_str}{error_format_str})),"
- )
else:
retval.append(
" 401: ClientAuthenticationError, 404: ResourceNotFoundError, 409: ResourceExistsError, "
diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2
index 2bea913ef3..fd2cbdedc1 100644
--- a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2
+++ b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2
@@ -892,6 +892,23 @@ def _deserialize(
return _deserialize_with_callable(deserializer, value)
+def _failsafe_deserialize(
+ deserializer: typing.Any,
+ value: typing.Any,
+ module: typing.Optional[str] = None,
+ rf: typing.Optional["_RestField"] = None,
+ format: typing.Optional[str] = None,
+) -> typing.Any:
+ try:
+ return _deserialize(deserializer, value, module, rf, format)
+ except DeserializationError:
+ _LOGGER.warning(
+ "Ran into a deserialization error. Ignoring since this is failsafe deserialization",
+ exc_info=True
+ )
+ return None
+
+
class _RestField:
def __init__(
self,
diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json
index 500223ac08..4acecf6d8a 100644
--- a/packages/http-client-python/package-lock.json
+++ b/packages/http-client-python/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@typespec/http-client-python",
- "version": "0.3.9",
+ "version": "0.4.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@typespec/http-client-python",
- "version": "0.3.9",
+ "version": "0.4.1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -21,7 +21,7 @@
"@azure-tools/typespec-azure-core": "~0.48.0",
"@azure-tools/typespec-azure-resource-manager": "~0.48.0",
"@azure-tools/typespec-azure-rulesets": "~0.48.0",
- "@azure-tools/typespec-client-generator-core": "~0.48.0",
+ "@azure-tools/typespec-client-generator-core": "~0.48.5",
"@types/js-yaml": "~4.0.5",
"@types/node": "~22.5.4",
"@types/semver": "7.5.8",
@@ -45,7 +45,7 @@
"@azure-tools/typespec-azure-core": ">=0.48.0 <1.0.0",
"@azure-tools/typespec-azure-resource-manager": ">=0.48.0 <1.0.0",
"@azure-tools/typespec-azure-rulesets": ">=0.48.0 <3.0.0",
- "@azure-tools/typespec-client-generator-core": ">=0.48.0 <1.0.0",
+ "@azure-tools/typespec-client-generator-core": ">=0.48.5 <1.0.0",
"@typespec/compiler": ">=0.62.0 <1.0.0",
"@typespec/http": ">=0.62.0 <1.0.0",
"@typespec/openapi": ">=0.62.0 <1.0.0",
@@ -237,9 +237,9 @@
}
},
"node_modules/@azure-tools/typespec-client-generator-core": {
- "version": "0.48.0",
- "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.48.0.tgz",
- "integrity": "sha512-+fmKjapz0kP7ONPZap8dgcIKIdQw+YBSrf89csbIyhPTcLnVAk/BKljo8FoNypKXwqKHenslLm0njBKPllkopg==",
+ "version": "0.48.5",
+ "resolved": "https://registry.npmjs.org/@azure-tools/typespec-client-generator-core/-/typespec-client-generator-core-0.48.5.tgz",
+ "integrity": "sha512-oAGyH99f3FMzTVE82A/hHupMlpDhxBUTL63wCUab9DM6Rqk+liBGobGl/EPdiOxpvcvhm1drEhkFCkqJt6JenA==",
"dev": true,
"dependencies": {
"change-case": "~5.4.4",
diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json
index 34627e8a8d..02c12bbc48 100644
--- a/packages/http-client-python/package.json
+++ b/packages/http-client-python/package.json
@@ -1,6 +1,6 @@
{
"name": "@typespec/http-client-python",
- "version": "0.3.12",
+ "version": "0.4.1",
"author": "Microsoft Corporation",
"description": "TypeSpec emitter for Python SDKs",
"homepage": "https://typespec.io",
@@ -60,7 +60,7 @@
"@azure-tools/typespec-azure-resource-manager": ">=0.48.0 <1.0.0",
"@azure-tools/typespec-autorest": ">=0.48.0 <1.0.0",
"@azure-tools/typespec-azure-rulesets": ">=0.48.0 <3.0.0",
- "@azure-tools/typespec-client-generator-core": ">=0.48.0 <1.0.0"
+ "@azure-tools/typespec-client-generator-core": ">=0.48.5 <1.0.0"
},
"dependencies": {
"js-yaml": "~4.1.0",
@@ -77,7 +77,7 @@
"@azure-tools/typespec-azure-core": "~0.48.0",
"@azure-tools/typespec-azure-rulesets": "~0.48.0",
"@azure-tools/typespec-azure-resource-manager": "~0.48.0",
- "@azure-tools/typespec-client-generator-core": "~0.48.0",
+ "@azure-tools/typespec-client-generator-core": "~0.48.5",
"@azure-tools/cadl-ranch-specs": "~0.39.1",
"@azure-tools/cadl-ranch-expect": "~0.15.6",
"@types/js-yaml": "~4.0.5",
diff --git a/packages/http-server-csharp/src/attributes.ts b/packages/http-server-csharp/src/attributes.ts
index de792e7402..cc4531d31f 100644
--- a/packages/http-server-csharp/src/attributes.ts
+++ b/packages/http-server-csharp/src/attributes.ts
@@ -419,13 +419,45 @@ export function getNumericConstraintAttribute(
export function getSafeIntAttribute(type: Scalar): Attribute | undefined {
if (type.name.toLowerCase() !== "safeint") return undefined;
- return new Attribute(
+ const attr: Attribute = new Attribute(
new AttributeType({
- name: "SafeInt",
+ name: `NumericConstraint`,
namespace: HelperNamespace,
}),
[],
);
+
+ attr.parameters.push(
+ new Parameter({
+ name: "MinValue",
+ value: new NumericValue(-9007199254740991),
+ optional: true,
+ type: new CSharpType({
+ name: "long",
+ namespace: "System",
+ isBuiltIn: true,
+ isValueType: true,
+ isNullable: false,
+ }),
+ }),
+ );
+
+ attr.parameters.push(
+ new Parameter({
+ name: "MaxValue",
+ value: new NumericValue(9007199254740991),
+ optional: true,
+ type: new CSharpType({
+ name: "long",
+ namespace: "System",
+ isBuiltIn: true,
+ isValueType: true,
+ isNullable: false,
+ }),
+ }),
+ );
+
+ return attr;
}
function getEnumAttribute(type: Enum, cSharpName?: string): Attribute {
diff --git a/packages/http-server-csharp/src/interfaces.ts b/packages/http-server-csharp/src/interfaces.ts
index de6f87a985..b7142c48a0 100644
--- a/packages/http-server-csharp/src/interfaces.ts
+++ b/packages/http-server-csharp/src/interfaces.ts
@@ -20,17 +20,20 @@ export class CSharpType implements CSharpTypeMetadata {
namespace: string;
isBuiltIn: boolean;
isValueType: boolean;
+ isNullable: boolean;
public constructor(input: {
name: string;
namespace: string;
isBuiltIn?: boolean;
isValueType?: boolean;
+ isNullable?: boolean;
}) {
this.name = input.name;
this.namespace = input.namespace;
this.isBuiltIn = input.isBuiltIn !== undefined ? input.isBuiltIn : input.namespace === "System";
this.isValueType = input.isValueType !== undefined ? input.isValueType : false;
+ this.isNullable = input.isNullable !== undefined ? input.isNullable : false;
}
isNamespaceInScope(scope?: Scope, visited?: Set>): boolean {
diff --git a/packages/http-server-csharp/src/service.ts b/packages/http-server-csharp/src/service.ts
index a64e9bf690..2eefa32289 100644
--- a/packages/http-server-csharp/src/service.ts
+++ b/packages/http-server-csharp/src/service.ts
@@ -346,7 +346,8 @@ export async function $onEmit(context: EmitContext)
property,
property.name,
);
- const [typeName, typeDefault] = this.#findPropertyType(property);
+
+ const [typeName, typeDefault, nullable] = this.#findPropertyType(property);
const doc = getDoc(this.emitter.getProgram(), property);
const attributes = getModelAttributes(this.emitter.getProgram(), property, propertyName);
// eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -356,7 +357,9 @@ export async function $onEmit(context: EmitContext)
: typeDefault;
return this.emitter.result
.rawCode(code`${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public ${this.#isInheritedProperty(property) ? "new " : ""}${typeName}${
- property.optional && isValueType(this.emitter.getProgram(), property.type) ? "?" : ""
+ isValueType(this.emitter.getProgram(), property.type) && (property.optional || nullable)
+ ? "?"
+ : ""
} ${propertyName} { get; ${typeDefault ? "}" : "set; }"}${
defaultValue ? ` = ${defaultValue};\n` : "\n"
}
@@ -365,14 +368,27 @@ export async function $onEmit(context: EmitContext)
#findPropertyType(
property: ModelProperty,
- ): [EmitterOutput, string | boolean | undefined] {
+ ): [EmitterOutput, string | boolean | undefined, boolean] {
return this.#getTypeInfoForTsType(property.type);
}
+ #getTypeInfoForUnion(
+ union: Union,
+ ): [EmitterOutput, string | boolean | undefined, boolean] {
+ const propResult = this.#getNonNullableTsType(union);
+ if (propResult === undefined) {
+ return [
+ code`${emitter.emitTypeReference(union)}`,
+ undefined,
+ [...union.variants.values()].filter((v) => isNullType(v.type)).length > 0,
+ ];
+ }
+ const [typeName, typeDefault, _] = this.#getTypeInfoForTsType(propResult.type);
+ return [typeName, typeDefault, propResult.nullable];
+ }
#getTypeInfoForTsType(
- this: any,
tsType: Type,
- ): [EmitterOutput, string | boolean | undefined] {
+ ): [EmitterOutput, string | boolean | undefined, boolean] {
function extractStringValue(type: Type, span: StringTemplateSpan): string {
switch (type.kind) {
case "String":
@@ -403,54 +419,62 @@ export async function $onEmit(context: EmitContext)
}
switch (tsType.kind) {
case "String":
- return [code`string`, `"${tsType.value}"`];
+ return [code`string`, `"${tsType.value}"`, false];
case "StringTemplate":
const template = tsType;
if (template.stringValue !== undefined)
- return [code`string`, `"${template.stringValue}"`];
+ return [code`string`, `"${template.stringValue}"`, false];
const spanResults: string[] = [];
for (const span of template.spans) {
spanResults.push(extractStringValue(span, span));
}
- return [code`string`, `"${spanResults.join("")}"`];
+ return [code`string`, `"${spanResults.join("")}"`, false];
case "Boolean":
- return [code`bool`, `${tsType.value === true ? true : false}`];
+ return [code`bool`, `${tsType.value === true ? true : false}`, false];
case "Number":
const [type, value] = this.#findNumericType(tsType);
- return [code`${type}`, `${value}`];
+ return [code`${type}`, `${value}`, false];
case "Tuple":
const defaults = [];
const [csharpType, isObject] = this.#coalesceTypes(tsType.values);
- if (isObject) return ["object[]", undefined];
+ if (isObject) return ["object[]", undefined, false];
for (const value of tsType.values) {
const [_, itemDefault] = this.#getTypeInfoForTsType(value);
defaults.push(itemDefault);
}
- return [code`${csharpType.getTypeReference()}[]`, `[${defaults.join(", ")}]`];
+ return [
+ code`${csharpType.getTypeReference()}[]`,
+ `[${defaults.join(", ")}]`,
+ csharpType.isNullable,
+ ];
case "Object":
- return [code`object`, undefined];
+ return [code`object`, undefined, false];
case "Model":
if (this.#isRecord(tsType)) {
- return [code`JsonObject`, undefined];
+ return [code`JsonObject`, undefined, false];
}
- return [code`${emitter.emitTypeReference(tsType)}`, undefined];
+ return [code`${emitter.emitTypeReference(tsType)}`, undefined, false];
+ case "ModelProperty":
+ return this.#getTypeInfoForTsType(tsType.type);
case "Enum":
- return [code`${emitter.emitTypeReference(tsType)}`, undefined];
+ return [code`${emitter.emitTypeReference(tsType)}`, undefined, false];
case "EnumMember":
if (typeof tsType.value === "number") {
const stringValue = tsType.value.toString();
if (stringValue.includes(".") || stringValue.includes("e"))
- return ["double", stringValue];
- return ["int", stringValue];
+ return ["double", stringValue, false];
+ return ["int", stringValue, false];
}
if (typeof tsType.value === "string") {
- return ["string", tsType.value];
+ return ["string", tsType.value, false];
}
- return [code`object`, undefined];
+ return [code`object`, undefined, false];
case "Union":
- return [code`${emitter.emitTypeReference(tsType)}`, undefined];
+ return this.#getTypeInfoForUnion(tsType);
+ case "UnionVariant":
+ return this.#getTypeInfoForTsType(tsType.type);
default:
- return [code`${emitter.emitTypeReference(tsType)}`, undefined];
+ return [code`${emitter.emitTypeReference(tsType)}`, undefined, false];
}
}
@@ -753,13 +777,13 @@ export async function $onEmit(context: EmitContext)
}
let i = 1;
for (const requiredParam of requiredParams) {
- const [paramType, _] = this.#findPropertyType(requiredParam);
+ const [paramType, _, __] = this.#findPropertyType(requiredParam);
signature.push(
code`${paramType} ${ensureCSharpIdentifier(this.emitter.getProgram(), requiredParam, requiredParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`,
);
}
for (const optionalParam of optionalParams) {
- const [paramType, _] = this.#findPropertyType(optionalParam);
+ const [paramType, _, __] = this.#findPropertyType(optionalParam);
signature.push(
code`${paramType}? ${ensureCSharpIdentifier(this.emitter.getProgram(), optionalParam, optionalParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`,
);
@@ -896,7 +920,7 @@ export async function $onEmit(context: EmitContext)
name,
NameCasingType.Parameter,
);
- let [emittedType, emittedDefault] = this.#findPropertyType(parameter);
+ let [emittedType, emittedDefault, _] = this.#findPropertyType(parameter);
if (emittedType.toString().endsWith("[]")) emittedDefault = undefined;
// eslint-disable-next-line @typescript-eslint/no-deprecated
const defaultValue = parameter.default
@@ -907,11 +931,18 @@ export async function $onEmit(context: EmitContext)
code`${httpParam.type !== "path" ? this.#emitParameterAttribute(httpParam) : ""}${emittedType} ${emittedName}${defaultValue === undefined ? "" : ` = ${defaultValue}`}`,
);
}
+ #getBodyParameters(operation: HttpOperation): ModelProperty[] | undefined {
+ const bodyParam = operation.parameters.body;
+ if (bodyParam === undefined) return undefined;
+ if (bodyParam.property !== undefined) return [bodyParam.property];
+ if (bodyParam.type.kind !== "Model" || bodyParam.type.properties.size < 1) return undefined;
+ return [...bodyParam.type.properties.values()];
+ }
#emitOperationCallParameters(operation: HttpOperation): EmitterOutput {
const signature = new StringBuilder();
- const bodyParam = operation.parameters.body;
let i = 0;
+ const bodyParameters = this.#getBodyParameters(operation);
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
for (const parameter of operation.parameters.parameters) {
i++;
@@ -922,13 +953,27 @@ export async function $onEmit(context: EmitContext)
) {
signature.push(
code`${this.#emitOperationCallParameter(operation, parameter)}${
- i < operation.parameters.parameters.length || bodyParam !== undefined ? ", " : ""
+ i < operation.parameters.parameters.length || bodyParameters !== undefined ? ", " : ""
}`,
);
}
}
- if (bodyParam !== undefined) {
- signature.push(code`body`);
+ if (bodyParameters !== undefined) {
+ if (bodyParameters.length === 1) {
+ signature.push(code`body`);
+ } else {
+ let j = 0;
+ for (const parameter of bodyParameters) {
+ j++;
+ const propertyName = ensureCSharpIdentifier(
+ this.emitter.getProgram(),
+ parameter,
+ parameter.name,
+ NameCasingType.Property,
+ );
+ signature.push(code`body?.${propertyName}${j < bodyParameters.length ? ", " : ""}`);
+ }
+ }
}
return signature.reduce();
@@ -1148,6 +1193,14 @@ export async function $onEmit(context: EmitContext)
return result;
}
+ #getNonNullableTsType(union: Union): { type: Type; nullable: boolean } | undefined {
+ const types = [...union.variants.values()];
+ const nulls = types.flatMap((v) => v.type).filter((t) => isNullType(t));
+ const nonNulls = types.flatMap((v) => v.type).filter((t) => !isNullType(t));
+ if (nonNulls.length === 1) return { type: nonNulls[0], nullable: nulls.length > 0 };
+ return undefined;
+ }
+
#coalesceTypes(types: Type[]): [CSharpType, boolean] {
const defaultValue: [CSharpType, boolean] = [
new CSharpType({
@@ -1158,8 +1211,9 @@ export async function $onEmit(context: EmitContext)
true,
];
let current: CSharpType | undefined = undefined;
+ let nullable: boolean = false;
for (const type of types) {
- let candidate: CSharpType;
+ let candidate: CSharpType | undefined = undefined;
switch (type.kind) {
case "Boolean":
candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
@@ -1186,14 +1240,24 @@ export async function $onEmit(context: EmitContext)
case "Scalar":
candidate = getCSharpTypeForScalar(this.emitter.getProgram(), type);
break;
+ case "Intrinsic":
+ if (isNullType(type)) {
+ nullable = true;
+ candidate = current;
+ } else {
+ return defaultValue;
+ }
+ break;
default:
return defaultValue;
}
current = current ?? candidate;
- if (current === undefined || !candidate.equals(current)) return defaultValue;
+ if (current === undefined || (candidate !== undefined && !candidate.equals(current)))
+ return defaultValue;
}
+ if (current !== undefined && nullable) current.isNullable = true;
return current === undefined ? defaultValue : [current, false];
}
diff --git a/packages/http-server-csharp/src/utils.ts b/packages/http-server-csharp/src/utils.ts
index c345973b7c..ff667b0ca0 100644
--- a/packages/http-server-csharp/src/utils.ts
+++ b/packages/http-server-csharp/src/utils.ts
@@ -41,10 +41,13 @@ import {
} from "./interfaces.js";
import { reportDiagnostic } from "./lib.js";
+const _scalars: Map = new Map();
export function getCSharpTypeForScalar(program: Program, scalar: Scalar): CSharpType {
+ if (_scalars.has(scalar)) return _scalars.get(scalar)!;
if (program.checker.isStdType(scalar)) {
return getCSharpTypeForStdScalars(program, scalar);
}
+
if (scalar.baseScalar) {
return getCSharpTypeForScalar(program, scalar.baseScalar);
}
@@ -54,12 +57,16 @@ export function getCSharpTypeForScalar(program: Program, scalar: Scalar): CSharp
format: { typeName: scalar.name },
target: scalar,
});
- return new CSharpType({
+
+ const result = new CSharpType({
name: "Object",
namespace: "System",
isBuiltIn: true,
isValueType: false,
});
+
+ _scalars.set(scalar, result);
+ return result;
}
export const UnknownType: CSharpType = new CSharpType({
@@ -71,7 +78,7 @@ export const UnknownType: CSharpType = new CSharpType({
export function getCSharpType(
program: Program,
type: Type,
- namespace: string,
+ namespace?: string,
): { type: CSharpType; value?: CSharpValue } | undefined {
const known = getKnownType(program, type);
if (known !== undefined) return { type: known };
@@ -118,7 +125,7 @@ export function getCSharpType(
return {
type: new CSharpType({
name: ensureCSharpIdentifier(program, type, type.name, NameCasingType.Class),
- namespace: namespace,
+ namespace: namespace || "Models",
isBuiltIn: false,
isValueType: false,
}),
@@ -167,24 +174,26 @@ export function getCSharpType(
export function coalesceTypes(
program: Program,
types: Type[],
- namespace: string,
+ namespace?: string,
): { type: CSharpType; value?: CSharpValue } {
const visited = new Map();
let candidateType: CSharpType | undefined = undefined;
let candidateValue: CSharpValue | undefined = undefined;
for (const type of types) {
- if (!visited.has(type)) {
- const resolvedType = getCSharpType(program, type, namespace);
- if (resolvedType === undefined) return { type: UnknownType };
- if (resolvedType.type === UnknownType) return resolvedType;
- if (candidateType === undefined) {
- candidateType = resolvedType.type;
- candidateValue = resolvedType.value;
- } else {
- if (candidateValue !== resolvedType.value) candidateValue = undefined;
- if (candidateType !== resolvedType.type) return { type: UnknownType };
+ if (!isNullType(type)) {
+ if (!visited.has(type)) {
+ const resolvedType = getCSharpType(program, type, namespace);
+ if (resolvedType === undefined) return { type: UnknownType };
+ if (resolvedType.type === UnknownType) return resolvedType;
+ if (candidateType === undefined) {
+ candidateType = resolvedType.type;
+ candidateValue = resolvedType.value;
+ } else {
+ if (candidateValue !== resolvedType.value) candidateValue = undefined;
+ if (candidateType !== resolvedType.type) return { type: UnknownType };
+ }
+ visited.set(type, resolvedType);
}
- visited.set(type, resolvedType);
}
}
@@ -364,8 +373,11 @@ export function getCSharpTypeForStdScalars(
program: Program,
scalar: Scalar & { name: ExtendedIntrinsicScalarName },
): CSharpType {
+ const cached: CSharpType | undefined = _scalars.get(scalar);
+ if (cached !== undefined) return cached;
const builtIn: CSharpType | undefined = standardScalars.get(scalar.name);
if (builtIn !== undefined) {
+ _scalars.set(scalar, builtIn);
if (scalar.name === "numeric" || scalar.name === "integer" || scalar.name === "float") {
reportDiagnostic(program, {
code: "no-numeric",
@@ -390,10 +402,18 @@ export function getCSharpTypeForStdScalars(
}
export function isValueType(program: Program, type: Type): boolean {
- if (type.kind === "Boolean" || type.kind === "Number" || type.kind === "Enum") return true;
- if (type.kind !== "Scalar") return false;
- const scalarType = getCSharpTypeForScalar(program, type);
- return scalarType.isValueType;
+ if (
+ type.kind === "Boolean" ||
+ type.kind === "Number" ||
+ type.kind === "Enum" ||
+ type.kind === "EnumMember"
+ )
+ return true;
+ if (type.kind === "Scalar") return getCSharpTypeForScalar(program, type).isValueType;
+ if (type.kind !== "Union") return false;
+ return [...type.variants.values()]
+ .flatMap((v) => v.type)
+ .every((t) => isNullType(t) || isValueType(program, t));
}
export function formatComment(
diff --git a/packages/http-server-csharp/test/generation.test.ts b/packages/http-server-csharp/test/generation.test.ts
index cc1f17441b..78328daf5b 100644
--- a/packages/http-server-csharp/test/generation.test.ts
+++ b/packages/http-server-csharp/test/generation.test.ts
@@ -1024,3 +1024,77 @@ it("generates valid code for anonymous models", async () => {
],
);
});
+
+it("handles nullable types correctly", async () => {
+ await compileAndValidateMultiple(
+ runner,
+ `
+ /** A simple test model*/
+ model Foo {
+ /** Nullable numeric property */
+ intProp: int32 | null;
+ /** Nullable reference type */
+ stringProp: string | null;
+ #suppress "@typespec/http-server-csharp/anonymous-model" "This is a test"
+ /** A complex property */
+ modelProp: {
+ bar: string;
+ } | null;
+ #suppress "@typespec/http-server-csharp/anonymous-model" "This is a test"
+ anotherModelProp: {
+ baz: string;
+ };
+
+ yetAnother: Foo.modelProp | null;
+
+ }
+
+ @route("/foo") op foo(): void;
+ `,
+ [
+ ["Model0.cs", ["public partial class Model0", "public string Bar { get; set; }"]],
+ ["Model1.cs", ["public partial class Model1", "public string Baz { get; set; }"]],
+ [
+ "Foo.cs",
+ [
+ "public partial class Foo",
+ "public int? IntProp { get; set; }",
+ "public string StringProp { get; set; }",
+ "public Model0 ModelProp { get; set; }",
+ "public Model1 AnotherModelProp { get; set; }",
+ "public Model0 YetAnother { get; set; }",
+ ],
+ ],
+ ["ContosoOperationsControllerBase.cs", [`public virtual async Task Foo()`]],
+ ["IContosoOperations.cs", [`Task FooAsync( );`]],
+ ],
+ );
+});
+
+it("handles implicit request body models correctly", async () => {
+ await compileAndValidateMultiple(
+ runner,
+ `
+ #suppress "@typespec/http-server-csharp/anonymous-model" "Test"
+ @route("/foo") @post op foo(intProp?: int32, arrayProp?: string[]): void;
+ `,
+ [
+ [
+ "Model0.cs",
+ [
+ "public partial class Model0",
+ "public int? IntProp { get; set; }",
+ "public string[] ArrayProp { get; set; }",
+ ],
+ ],
+ [
+ "ContosoOperationsControllerBase.cs",
+ [
+ `public virtual async Task Foo(Model0 body)`,
+ ".FooAsync(body?.IntProp, body?.ArrayProp)",
+ ],
+ ],
+ ["IContosoOperations.cs", [`Task FooAsync( int? intProp, string[]? arrayProp);`]],
+ ],
+ );
+});
diff --git a/packages/http-specs/CHANGELOG.md b/packages/http-specs/CHANGELOG.md
index 2bfe98d87a..a64b980f23 100644
--- a/packages/http-specs/CHANGELOG.md
+++ b/packages/http-specs/CHANGELOG.md
@@ -1,5 +1,9 @@
# @typespec/http-specs
+## 0.1.0-alpha.3
+
+- Create coverages container if not existing
+
## 0.1.0-alpha.2
- Minor `api-key` in the `authentication` specs
diff --git a/packages/http-specs/package.json b/packages/http-specs/package.json
index 33c0d78243..18ad38dd70 100644
--- a/packages/http-specs/package.json
+++ b/packages/http-specs/package.json
@@ -1,6 +1,6 @@
{
"name": "@typespec/http-specs",
- "version": "0.1.0-alpha.2",
+ "version": "0.1.0-alpha.3",
"description": "Spec scenarios and mock apis",
"main": "dist/index.js",
"type": "module",
@@ -12,6 +12,8 @@
"validate-scenarios": "tsp-spector validate-scenarios ./specs",
"generate-scenarios-summary": "tsp-spector generate-scenarios-summary ./specs",
"regen-docs": "pnpm generate-scenarios-summary",
+ "upload-manifest": "tsp-spector upload-manifest ./specs --setName @typespec/http-specs --containerName manifests-typespec --storageAccountName typespec",
+ "upload-coverage": "tsp-spector upload-coverage --generatorName @typespec/http-specs --generatorVersion 0.1.0-alpha.3 --containerName coverages --generatorMode standard --storageAccountName typespec",
"validate-mock-apis": "tsp-spector validate-mock-apis ./specs",
"check-scenario-coverage": "tsp-spector check-coverage ./specs",
"validate-client-server": "concurrently \"tsp-spector server start ./specs\" \"npm run client\" && tsp-spector server stop",
diff --git a/packages/http-specs/spec-summary.md b/packages/http-specs/spec-summary.md
index 9fbaa6e661..ff4a3ec4d5 100644
--- a/packages/http-specs/spec-summary.md
+++ b/packages/http-specs/spec-summary.md
@@ -4360,77 +4360,6 @@ Expected input body:
}
```
-### Type_Model_Templated_float32Type
-
-- Endpoint: `put /type/model/templated/float32ValuesType`
-
-Expected input body:
-
-```json
-{
- "kind": "Float32Values",
- "values": [0.5],
- "value": 0.5
-}
-```
-
-Expected response body:
-
-```json
-{
- "kind": "Float32Values",
- "values": [0.5],
- "value": 0.5
-}
-```
-
-### Type_Model_Templated_int32Type
-
-- Endpoint: `put /type/model/templated/int32ValuesType`
-
-Expected input body:
-
-```json
-{
- "kind": "Int32Values",
- "values": [1234],
- "value": 1234
-}
-```
-
-Expected response body:
-
-```json
-{
- "kind": "Int32Values",
- "values": [1234],
- "value": 1234
-}
-```
-
-### Type_Model_Templated_numericType
-
-- Endpoint: `put /type/model/templated/numericType`
-
-Expected input body:
-
-```json
-{
- "kind": "Int32Values",
- "values": [1234],
- "value": 1234
-}
-```
-
-Expected response body:
-
-```json
-{
- "values": [1234],
- "value": 1234
-}
-```
-
### Type_Model_Usage_input
- Endpoint: `get /type/model/usage/input`
@@ -7436,6 +7365,49 @@ Expected request body:
{ "prop": "foo" }
```
+### Versioning_Removed_modelV3
+
+- Endpoint: `post /versioning/removed/api-version:{version}/v3`
+
+path: "/versioning/removed/api-version[:]v1/v3"
+Expected request body:
+
+```json
+{ "id": "123", "enumProp": "enumMemberV1" }
+```
+
+Expected response body:
+
+```json
+{ "id": "123", "enumProp": "enumMemberV1" }
+```
+
+path: "/versioning/removed/api-version[:]v2preview/v3"
+Expected request body:
+
+```json
+{ "id": "123" }
+```
+
+Expected response body:
+
+```json
+{ "id": "123" }
+```
+
+path: "/versioning/removed/api-version[:]v2/v3"
+Expected request body:
+
+```json
+{ "id": "123", "enumProp": "enumMemberV1" }
+```
+
+Expected response body:
+
+```json
+{ "id": "123", "enumProp": "enumMemberV1" }
+```
+
### Versioning_Removed_v2
- Endpoint: `post /versioning/removed/api-version:{version}/v2`
diff --git a/packages/http-specs/specs/type/model/templated/main.tsp b/packages/http-specs/specs/type/model/templated/main.tsp
deleted file mode 100644
index 3d9bc1d92e..0000000000
--- a/packages/http-specs/specs/type/model/templated/main.tsp
+++ /dev/null
@@ -1,130 +0,0 @@
-import "@typespec/http";
-import "@typespec/spector";
-
-using Http;
-using Spector;
-
-/**
- * Illustrates the model templated cases. There is a base templated type and an instantiated type extending from it.
- */
-@scenarioService("/type/model/templated")
-namespace Type.Model.Templated;
-
-@friendlyName("{name}Type", T)
-model NumericType {
- /**
- * An array of numeric values.
- */
- values: T[];
-
- value: T;
-}
-
-/**
- * An instantiated type representing int32 values type.
- */
-model Int32ValuesType extends NumericType {
- /**
- * The Kind of the Int32ValuesType.
- */
- kind: "Int32Values";
-}
-
-/**
- * An instantiated type representing float32 values type.
- */
-model Float32ValuesType extends NumericType {
- /**
- * The Kind of the Float32ValuesType.
- */
- kind: "Float32Values";
-}
-
-@scenario
-@scenarioDoc("""
- Expected input body:
- ```json
- {
- "kind": "Int32Values",
- "values":
- [
- 1234
- ],
- "value": 1234
- }
- ```
-
- Expected response body:
- ```json
- {
- "values":
- [
- 1234
- ],
- "value": 1234
- }
- ```
- """)
-@route("/numericType")
-@put
-op numericType(@body input: NumericType): NumericType;
-
-@scenario
-@scenarioDoc("""
- Expected input body:
- ```json
- {
- "kind": "Float32Values",
- "values":
- [
- 0.5
- ],
- "value": 0.5
- }
- ```
-
- Expected response body:
- ```json
- {
- "kind": "Float32Values",
- "values":
- [
- 0.5
- ],
- "value": 0.5
- }
- ```
- """)
-@route("/float32ValuesType")
-@put
-op float32Type(@body input: Float32ValuesType): Float32ValuesType;
-
-@scenario
-@scenarioDoc("""
- Expected input body:
- ```json
- {
- "kind": "Int32Values",
- "values":
- [
- 1234
- ],
- "value": 1234
- }
- ```
-
- Expected response body:
- ```json
- {
- "kind": "Int32Values",
- "values":
- [
- 1234
- ],
- "value": 1234
- }
- ```
- """)
-@route("/int32ValuesType")
-@put
-op int32Type(@body input: Int32ValuesType): Int32ValuesType;
diff --git a/packages/http-specs/specs/type/model/templated/mockapi.ts b/packages/http-specs/specs/type/model/templated/mockapi.ts
deleted file mode 100644
index cd69b9bfa8..0000000000
--- a/packages/http-specs/specs/type/model/templated/mockapi.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { json, passOnSuccess, ScenarioMockApi } from "@typespec/spec-api";
-
-export const Scenarios: Record = {};
-
-Scenarios.Type_Model_Templated_numericType = passOnSuccess({
- uri: "/type/model/templated/numericType",
- method: "put",
- request: {
- body: {
- kind: "Int32Values",
- values: [1234],
- value: 1234,
- },
- },
- response: {
- status: 200,
- body: json({
- kind: "Int32Values",
- values: [1234],
- value: 1234,
- }),
- },
- kind: "MockApiDefinition",
-});
-
-Scenarios.Type_Model_Templated_float32Type = passOnSuccess({
- uri: "/type/model/templated/float32ValuesType",
- method: "put",
- request: {
- body: {
- kind: "Float32Values",
- values: [0.5],
- value: 0.5,
- },
- },
- response: {
- status: 200,
- body: json({
- kind: "Float32Values",
- values: [0.5],
- value: 0.5,
- }),
- },
- kind: "MockApiDefinition",
-});
-
-Scenarios.Type_Model_Templated_int32Type = passOnSuccess({
- uri: "/type/model/templated/int32ValuesType",
- method: "put",
- request: {
- body: {
- kind: "Int32Values",
- values: [1234],
- value: 1234,
- },
- },
- response: {
- status: 200,
- body: json({
- kind: "Int32Values",
- values: [1234],
- value: 1234,
- }),
- },
- kind: "MockApiDefinition",
-});
diff --git a/packages/http-specs/specs/versioning/removed/main.tsp b/packages/http-specs/specs/versioning/removed/main.tsp
index 609df7c6d3..17bb7b0be2 100644
--- a/packages/http-specs/specs/versioning/removed/main.tsp
+++ b/packages/http-specs/specs/versioning/removed/main.tsp
@@ -21,7 +21,7 @@ using TypeSpec.Versioning;
endpoint: url,
/**
- * Need to be set as 'v1' or 'v2' in client.
+ * Need to be set as 'v1', 'v2preview' or 'v2' in client.
*/
version: Versions,
}
@@ -37,6 +37,11 @@ enum Versions {
*/
v1: "v1",
+ /**
+ * The V2 Preview version.
+ */
+ v2preview: "v2preview",
+
/**
* The version v2.
*/
@@ -67,6 +72,14 @@ model ModelV2 {
unionProp: UnionV2;
}
+model ModelV3 {
+ id: string;
+
+ @removed(Versions.v2preview)
+ @added(Versions.v2)
+ enumProp: EnumV3;
+}
+
enum EnumV2 {
@removed(Versions.v2)
enumMemberV1,
@@ -74,6 +87,14 @@ enum EnumV2 {
enumMemberV2,
}
+enum EnumV3 {
+ @removed(Versions.v2preview)
+ @added(Versions.v2)
+ enumMemberV1,
+
+ enumMemberV2Preview,
+}
+
@removed(Versions.v2)
union UnionV1 {
string,
@@ -124,3 +145,44 @@ interface InterfaceV1 {
@route("/v1")
v1InInterface(@body body: ModelV1): ModelV1;
}
+
+/** This operation will pass different paths and different request bodies based on different versions. */
+@scenario
+@scenarioDoc("""
+ path: "/versioning/removed/api-version[:]v1/v3"
+ Expected request body:
+ ```json
+ { "id": "123", "enumProp": "enumMemberV1" }
+ ```
+
+ Expected response body:
+ ```json
+ { "id": "123", "enumProp": "enumMemberV1" }
+ ```
+
+ path: "/versioning/removed/api-version[:]v2preview/v3"
+ Expected request body:
+ ```json
+ { "id": "123"}
+ ```
+
+ Expected response body:
+ ```json
+ { "id": "123"}
+ ```
+
+ path: "/versioning/removed/api-version[:]v2/v3"
+ Expected request body:
+ ```json
+ { "id": "123", "enumProp": "enumMemberV1" }
+ ```
+
+ Expected response body:
+ ```json
+ { "id": "123", "enumProp": "enumMemberV1" }
+ ```
+
+ """)
+@post
+@route("/v3")
+op modelV3(@body body: ModelV3): ModelV3;
diff --git a/packages/http-specs/specs/versioning/removed/mockapi.ts b/packages/http-specs/specs/versioning/removed/mockapi.ts
index 7f4e2534cf..c197f161d0 100644
--- a/packages/http-specs/specs/versioning/removed/mockapi.ts
+++ b/packages/http-specs/specs/versioning/removed/mockapi.ts
@@ -18,3 +18,50 @@ Scenarios.Versioning_Removed_v2 = passOnSuccess({
},
kind: "MockApiDefinition",
});
+
+Scenarios.Versioning_Removed_modelV3 = passOnSuccess({
+ uri: `/versioning/removed/api-version[:]v1/v3`,
+ method: "post",
+ request: {
+ body: {
+ id: "123",
+ enumProp: "enumMemberV1",
+ },
+ },
+ response: {
+ status: 200,
+ body: json({ id: "123", enumProp: "enumMemberV1" }),
+ },
+ kind: "MockApiDefinition",
+});
+
+Scenarios.Versioning_Removed_modelV3_V2 = passOnSuccess({
+ uri: `/versioning/removed/api-version[:]v2/v3`,
+ method: "post",
+ request: {
+ body: {
+ id: "123",
+ enumProp: "enumMemberV1",
+ },
+ },
+ response: {
+ status: 200,
+ body: json({ id: "123", enumProp: "enumMemberV1" }),
+ },
+ kind: "MockApiDefinition",
+});
+
+Scenarios.Versioning_Removed_modelV3_V2preview = passOnSuccess({
+ uri: `/versioning/removed/api-version[:]v2preview/v3`,
+ method: "post",
+ request: {
+ body: {
+ id: "123",
+ },
+ },
+ response: {
+ status: 200,
+ body: json({ id: "123" }),
+ },
+ kind: "MockApiDefinition",
+});
diff --git a/packages/spector/src/actions/upload-coverage-report.ts b/packages/spector/src/actions/upload-coverage-report.ts
index 9f5fb38ed4..caf1b20191 100644
--- a/packages/spector/src/actions/upload-coverage-report.ts
+++ b/packages/spector/src/actions/upload-coverage-report.ts
@@ -30,6 +30,7 @@ export async function uploadCoverageReport({
credential: new AzureCliCredential(),
containerName,
});
+ await client.createIfNotExists();
const generatorMetadata: GeneratorMetadata = {
name: generatorName,
version: generatorVersion,
diff --git a/website/src/content/docs/docs/getting-started/getting-started.md b/website/src/content/docs/docs/getting-started/getting-started.md
index 2a57a3236b..acf2caa3d4 100644
--- a/website/src/content/docs/docs/getting-started/getting-started.md
+++ b/website/src/content/docs/docs/getting-started/getting-started.md
@@ -3,4 +3,4 @@ id: getting-started
title: Getting Started
---
-- [Get started with TypeSpec for REST](./getting-started-rest/01-setup-basic-syntax.md)
+- [Get started with TypeSpec for REST](./getting-started-rest/01-setup-basic-syntax.mdx)