From a4b175ecd09d51822931b55ca1b3ee6ec06d5dcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:55:30 +0000 Subject: [PATCH 1/7] Initial plan From 897f92d0214f4058842126428b0a81f8ec1a423f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 19:59:58 +0000 Subject: [PATCH 2/7] Add support for 3xx status codes (including 302 redirect) Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- .../src/Providers/RestClientProvider.cs | 2 +- .../specs/response/redirect/main.tsp | 32 +++++++++++++++++++ .../specs/response/redirect/mockapi.ts | 18 +++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 packages/http-specs/specs/response/redirect/main.tsp create mode 100644 packages/http-specs/specs/response/redirect/mockapi.ts diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs index 2ed3406ed73..832dc6d83c6 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs @@ -859,7 +859,7 @@ private static List GetSuccessStatusCodes(InputOperation operation) foreach (var statusCode in response.StatusCodes) { - if (statusCode >= 200 && statusCode < 300) + if (statusCode >= 200 && statusCode < 400) { statusCodes.Add(statusCode); } diff --git a/packages/http-specs/specs/response/redirect/main.tsp b/packages/http-specs/specs/response/redirect/main.tsp new file mode 100644 index 00000000000..b8889f7d319 --- /dev/null +++ b/packages/http-specs/specs/response/redirect/main.tsp @@ -0,0 +1,32 @@ +import "@typespec/http"; +import "@typespec/spector"; + +using Http; +using Spector; + +/** + * Test for HTTP redirect status codes. + */ +@scenarioService("/response/redirect") +namespace Response.Redirect; + +@scenario +@scenarioDoc(""" + Test case for 302 redirect response. + + Verify that the client can handle a 302 redirect response with a Location header. + This is commonly used in OAuth-style authorization flows where the service redirects + users to a mobile operator's network for authentication. + + Expected status code 302 and response headers: + - Location: /redirected + """) +@route("/302") +@get +op redirect302(): { + @statusCode + _: 302; + + @header + location: url; +}; diff --git a/packages/http-specs/specs/response/redirect/mockapi.ts b/packages/http-specs/specs/response/redirect/mockapi.ts new file mode 100644 index 00000000000..68cb86960e3 --- /dev/null +++ b/packages/http-specs/specs/response/redirect/mockapi.ts @@ -0,0 +1,18 @@ +import { passOnSuccess, ScenarioMockApi, MockApiDefinition } from "@typespec/spector/http"; + +export const Scenarios: Record = {}; + +const validRedirect302: MockApiDefinition = { + uri: "/response/redirect/302", + method: "get", + request: {}, + response: { + status: 302, + headers: { + location: "/redirected", + }, + }, + kind: "MockApiDefinition", +}; + +Scenarios.Response_Redirect_redirect302 = passOnSuccess(validRedirect302); From e9fac6a4fbca1f3a57ebeda81ecfba5ab3128c60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:05:51 +0000 Subject: [PATCH 3/7] Add unit test for 302 redirect status code support Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- .../RestClientProviderTests.cs | 50 +++++++++++++++++++ .../http/response/redirect/Configuration.json | 5 ++ .../http/response/redirect/tsp-location.yaml | 1 + .../specs/response/redirect/mockapi.ts | 2 +- 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json create mode 100644 packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs index 57185131052..0e7ab7612a0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs @@ -102,6 +102,56 @@ public void ValidateProperties() Assert.IsFalse(propertyHash.ContainsKey("PipelineMessageClassifier204")); } + [Test] + public void Validate3xxRedirectStatusCode() + { + // Test that 3xx status codes (like 302 redirect) are handled correctly + var inputServiceMethod = InputFactory.BasicServiceMethod( + "TestRedirect", + InputFactory.Operation( + "Redirect302", + responses: + [ + InputFactory.OperationResponse( + statusCodes: [302], + headers: + [ + new InputOperationResponseHeader( + "location", + "location", + "Location header for redirect", + null, + InputPrimitiveType.String) + ]) + ])); + + var inputClient = InputFactory.Client("TestClient", methods: [inputServiceMethod]); + var clientProvider = new ClientProvider(inputClient); + var restClient = clientProvider.RestClient; + + Assert.IsNotNull(restClient); + + // Validate that the classifier for 302 status code exists + Dictionary propertyHash = restClient.Properties.ToDictionary(p => p.Name); + Assert.IsTrue(propertyHash.ContainsKey("PipelineMessageClassifier302"), + "PipelineMessageClassifier302 should be present for 302 redirect"); + + var pipelineMessageClassifier302 = propertyHash["PipelineMessageClassifier302"]; + Assert.AreEqual("PipelineMessageClassifier", pipelineMessageClassifier302.Type.Name); + Assert.AreEqual("PipelineMessageClassifier302", pipelineMessageClassifier302.Name); + Assert.AreEqual(MethodSignatureModifiers.Private | MethodSignatureModifiers.Static, pipelineMessageClassifier302.Modifiers); + + // Validate that fields are created correctly + Dictionary fieldHash = restClient.Fields.ToDictionary(f => f.Name); + Assert.IsTrue(fieldHash.ContainsKey("_pipelineMessageClassifier302"), + "_pipelineMessageClassifier302 field should be present for 302 redirect"); + + var pipelineMessageClassifier302Field = fieldHash["_pipelineMessageClassifier302"]; + Assert.AreEqual("PipelineMessageClassifier", pipelineMessageClassifier302Field.Type.Name); + Assert.AreEqual("_pipelineMessageClassifier302", pipelineMessageClassifier302Field.Name); + Assert.AreEqual(FieldModifiers.Private | FieldModifiers.Static, pipelineMessageClassifier302Field.Modifiers); + } + [TestCaseSource(nameof(GetMethodParametersTestCases))] public void TestGetMethodParameters(InputServiceMethod inputServiceMethod) { diff --git a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json new file mode 100644 index 00000000000..a702a6dddc3 --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json @@ -0,0 +1,5 @@ +{ + "output-folder": ".", + "namespace": "Response.Redirect", + "library-name": "Response.Redirect" +} diff --git a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml new file mode 100644 index 00000000000..cd08aae505e --- /dev/null +++ b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml @@ -0,0 +1 @@ +directory: ../../../../../../../http-specs/specs/response/redirect diff --git a/packages/http-specs/specs/response/redirect/mockapi.ts b/packages/http-specs/specs/response/redirect/mockapi.ts index 68cb86960e3..188cedcb47c 100644 --- a/packages/http-specs/specs/response/redirect/mockapi.ts +++ b/packages/http-specs/specs/response/redirect/mockapi.ts @@ -1,4 +1,4 @@ -import { passOnSuccess, ScenarioMockApi, MockApiDefinition } from "@typespec/spector/http"; +import { passOnSuccess, ScenarioMockApi, MockApiDefinition } from "@typespec/spec-api"; export const Scenarios: Record = {}; From 939090e49c98174d965348e2570bad03c4301530 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:12:35 +0000 Subject: [PATCH 4/7] Clarify 302 redirect documentation based on code review Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- packages/http-specs/specs/response/redirect/main.tsp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/http-specs/specs/response/redirect/main.tsp b/packages/http-specs/specs/response/redirect/main.tsp index b8889f7d319..ad32525838a 100644 --- a/packages/http-specs/specs/response/redirect/main.tsp +++ b/packages/http-specs/specs/response/redirect/main.tsp @@ -14,9 +14,9 @@ namespace Response.Redirect; @scenarioDoc(""" Test case for 302 redirect response. - Verify that the client can handle a 302 redirect response with a Location header. - This is commonly used in OAuth-style authorization flows where the service redirects - users to a mobile operator's network for authentication. + Verify that the client can receive and process a 302 redirect response with a Location header. + This is commonly used in OAuth-style authorization flows where the service provides a redirect + URI that the application can use to navigate users to the mobile operator's network for authentication. Expected status code 302 and response headers: - Location: /redirected From 5064523d6719d60e94cc98ee3f8af18120a1a079 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:28:26 +0000 Subject: [PATCH 5/7] Remove TestProjects files as requested Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- .../Spector/http/response/redirect/Configuration.json | 5 ----- .../Spector/http/response/redirect/tsp-location.yaml | 1 - 2 files changed, 6 deletions(-) delete mode 100644 packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json delete mode 100644 packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml diff --git a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json deleted file mode 100644 index a702a6dddc3..00000000000 --- a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/Configuration.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "output-folder": ".", - "namespace": "Response.Redirect", - "library-name": "Response.Redirect" -} diff --git a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml b/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml deleted file mode 100644 index cd08aae505e..00000000000 --- a/packages/http-client-csharp/generator/TestProjects/Spector/http/response/redirect/tsp-location.yaml +++ /dev/null @@ -1 +0,0 @@ -directory: ../../../../../../../http-specs/specs/response/redirect From 308cfa2ed5cd530a63ccee066824ffaac6573b6b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:32:04 +0000 Subject: [PATCH 6/7] Remove http-specs redirect directory as requested Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- .../specs/response/redirect/main.tsp | 32 ------------------- .../specs/response/redirect/mockapi.ts | 18 ----------- 2 files changed, 50 deletions(-) delete mode 100644 packages/http-specs/specs/response/redirect/main.tsp delete mode 100644 packages/http-specs/specs/response/redirect/mockapi.ts diff --git a/packages/http-specs/specs/response/redirect/main.tsp b/packages/http-specs/specs/response/redirect/main.tsp deleted file mode 100644 index ad32525838a..00000000000 --- a/packages/http-specs/specs/response/redirect/main.tsp +++ /dev/null @@ -1,32 +0,0 @@ -import "@typespec/http"; -import "@typespec/spector"; - -using Http; -using Spector; - -/** - * Test for HTTP redirect status codes. - */ -@scenarioService("/response/redirect") -namespace Response.Redirect; - -@scenario -@scenarioDoc(""" - Test case for 302 redirect response. - - Verify that the client can receive and process a 302 redirect response with a Location header. - This is commonly used in OAuth-style authorization flows where the service provides a redirect - URI that the application can use to navigate users to the mobile operator's network for authentication. - - Expected status code 302 and response headers: - - Location: /redirected - """) -@route("/302") -@get -op redirect302(): { - @statusCode - _: 302; - - @header - location: url; -}; diff --git a/packages/http-specs/specs/response/redirect/mockapi.ts b/packages/http-specs/specs/response/redirect/mockapi.ts deleted file mode 100644 index 188cedcb47c..00000000000 --- a/packages/http-specs/specs/response/redirect/mockapi.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { passOnSuccess, ScenarioMockApi, MockApiDefinition } from "@typespec/spec-api"; - -export const Scenarios: Record = {}; - -const validRedirect302: MockApiDefinition = { - uri: "/response/redirect/302", - method: "get", - request: {}, - response: { - status: 302, - headers: { - location: "/redirected", - }, - }, - kind: "MockApiDefinition", -}; - -Scenarios.Response_Redirect_redirect302 = passOnSuccess(validRedirect302); From 16228c00baaedb3503351ee9dfff13b75bacd92b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:35:22 +0000 Subject: [PATCH 7/7] Add test for CreateRequest method to verify classifier usage Co-authored-by: JoshLove-msft <54595583+JoshLove-msft@users.noreply.github.com> --- .../RestClientProviders/RestClientProviderTests.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs index 0e7ab7612a0..a563b215820 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/RestClientProviders/RestClientProviderTests.cs @@ -150,6 +150,16 @@ public void Validate3xxRedirectStatusCode() Assert.AreEqual("PipelineMessageClassifier", pipelineMessageClassifier302Field.Type.Name); Assert.AreEqual("_pipelineMessageClassifier302", pipelineMessageClassifier302Field.Name); Assert.AreEqual(FieldModifiers.Private | FieldModifiers.Static, pipelineMessageClassifier302Field.Modifiers); + + // Validate that the CreateRequest method uses the classifier + var createRequestMethod = restClient.Methods.FirstOrDefault(m => m.Signature.Name == "CreateRedirect302Request"); + Assert.IsNotNull(createRequestMethod, "CreateRedirect302Request method should exist"); + + var bodyStatements = createRequestMethod?.BodyStatements as MethodBodyStatements; + Assert.IsNotNull(bodyStatements, "Method body statements should not be null"); + + // Verify that the classifier property is referenced in the CreateRequest method body + ValidateResponseClassifier(bodyStatements!, "302"); } [TestCaseSource(nameof(GetMethodParametersTestCases))]