Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -717,10 +717,27 @@ private void AddUriSegments(
}

var path = pathSpan.Slice(0, paramIndex);
AppendLiteralSegment(uri, path.ToString(), statements);
pathSpan = pathSpan.Slice(paramIndex + 1);
var paramEndIndex = pathSpan.IndexOf('}');
var paramName = pathSpan.Slice(0, paramEndIndex).ToString();

/* An optional path parameter that is null must not leave a dangling
* path separator behind. For example "/foo/{bar}/{baz}" with an absent
* optional "baz" should produce "/foo/{bar}", not "/foo/{bar}/". When the
* upcoming parameter is optional, defer the trailing '/' of the preceding
* literal so it is only written together with the parameter value inside
* the null check below.
*/
var pathLiteral = path.ToString();
bool separatorDeferred = false;
if (pathLiteral.EndsWith('/')
&& inputParamMap.TryGetValue(paramName, out var optionalCheckParam)
&& optionalCheckParam is InputPathParameter { IsRequired: false })
{
pathLiteral = pathLiteral.Substring(0, pathLiteral.Length - 1);
separatorDeferred = true;
}
AppendLiteralSegment(uri, pathLiteral, statements);
/* when the parameter is in operation.uri, it is client parameter
* It is not operation parameter and not in inputParamHash list.
*/
Expand Down Expand Up @@ -766,7 +783,7 @@ private void AddUriSegments(
MethodBodyStatement statement;
if (inputParam?.IsRequired == false)
{
bool shouldPrependWithPathSeparator = path.Length > 0 && path[^1] != '/';
bool shouldPrependWithPathSeparator = separatorDeferred || (path.Length > 0 && path[^1] != '/');
List<MethodBodyStatement> appendPathStatements = shouldPrependWithPathSeparator
? [uri.AppendPath(Literal("/"), false).Terminate(), uri.AppendPath(valueExpression, escape).Terminate()]
: [uri.AppendPath(valueExpression, escape).Terminate()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3589,8 +3589,8 @@ public void ServerTemplateWithPathParameter_OnlyAppendsSegmentsAfterEndpoint()
MockHelpers.LoadMockGenerator();

var serverTemplate = "{endpoint}/{apiVersion}";
var apiVersionParam = InputFactory.PathParameter("apiVersion", InputPrimitiveType.String, serverUrlTemplate: serverTemplate);
var userIdParam = InputFactory.PathParameter("userId", InputPrimitiveType.String);
var apiVersionParam = InputFactory.PathParameter("apiVersion", InputPrimitiveType.String, isRequired: true, serverUrlTemplate: serverTemplate);
var userIdParam = InputFactory.PathParameter("userId", InputPrimitiveType.String, isRequired: true);

var operation = InputFactory.Operation(
name: "GetUser",
Expand Down Expand Up @@ -3633,7 +3633,7 @@ public void ServerTemplateWithMultipleSegments_HandlesCorrectly()
MockHelpers.LoadMockGenerator();

var serverTemplate = "{endpoint}/v1/services";
var operationIdParam = InputFactory.PathParameter("operationId", InputPrimitiveType.String);
var operationIdParam = InputFactory.PathParameter("operationId", InputPrimitiveType.String, isRequired: true);

var operation = InputFactory.Operation(
name: "GetOperation",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,34 @@ public void TestBuildCreateRequestMethodWithPathParameters()
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}

// An optional trailing path parameter must not emit a dangling separator when null.
// e.g. "/certificates/{certificateName}/{certificateVersion}" with a null version
// should produce "/certificates/{name}", not "/certificates/{name}/".
[Test]
public void TestBuildCreateRequestMethodWithOptionalPathParameter()
{
List<InputParameter> parameters =
[
InputFactory.PathParameter("certificateName", InputPrimitiveType.String, isRequired: true),
InputFactory.PathParameter("certificateVersion", InputPrimitiveType.String, isRequired: false),
];
var operation = InputFactory.Operation(
"getCertificate",
parameters: parameters,
uri: "/certificates/{certificateName}/{certificateVersion}");

var client = InputFactory.Client(
"TestClient",
methods: [InputFactory.BasicServiceMethod("Test", operation)]);

var clientProvider = new ClientProvider(client);
var restClientProvider = new MockClientProvider(client, clientProvider);

var writer = new TypeProviderWriter(restClientProvider);
var file = writer.Write();
Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content);
}

[Test]
public void TestBuildCreateRequestMethodWithQueryInPath()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// <auto-generated/>

#nullable disable

using System.ClientModel.Primitives;

namespace Sample
{
public partial class TestClient
{
internal global::System.ClientModel.Primitives.PipelineMessage CreateGetCertificateRequest(string certificateName, string certificateVersion, global::System.ClientModel.Primitives.RequestOptions options)
{
global::Sample.ClientUriBuilder uri = new global::Sample.ClientUriBuilder();
uri.Reset(_endpoint);
uri.AppendPath("/certificates/", false);
uri.AppendPath(certificateName, true);
if ((certificateVersion != null))
{
uri.AppendPath("/", false);
uri.AppendPath(certificateVersion, true);
}
global::System.ClientModel.Primitives.PipelineMessage message = Pipeline.CreateMessage(uri.ToUri(), "GET", PipelineMessageClassifier200);
global::System.ClientModel.Primitives.PipelineRequest request = message.Request;
message.Apply(options);
return message;
}
}
}
Loading