Skip to content

Commit 2d139cc

Browse files
Uri template fix (microsoft#7616)
1 parent ecf06e2 commit 2d139cc

File tree

2 files changed

+57
-2
lines changed

2 files changed

+57
-2
lines changed

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/RestClientProvider.cs

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -386,13 +386,47 @@ private IReadOnlyList<MethodBodyStatement> AppendPathParameters(ScopedApi uri, I
386386
{
387387
Dictionary<string, InputParameter> inputParamMap = new(operation.Parameters.ToDictionary(p => p.NameInRequest));
388388
List<MethodBodyStatement> statements = new(operation.Parameters.Count);
389-
string? endpoint = ClientProvider.EndpointParameterName;
390-
int uriOffset = endpoint is null || !operation.Uri.StartsWith(endpoint, StringComparison.Ordinal) ? 0 : endpoint.Length;
389+
int uriOffset = GetUriOffset(operation.Uri);
391390
AddUriSegments(operation.Uri, uriOffset, uri, statements, inputParamMap, paramMap, operation);
392391
AddUriSegments(operation.Path, 0, uri, statements, inputParamMap, paramMap, operation);
393392
return statements;
394393
}
395394

395+
private int GetUriOffset(string uriTemplate)
396+
{
397+
string? endpointParameter = ClientProvider.EndpointParameterName;
398+
if (endpointParameter == null)
399+
{
400+
return 0;
401+
}
402+
403+
ReadOnlySpan<char> templateSpan = uriTemplate.AsSpan();
404+
ReadOnlySpan<char> parameterSpan = endpointParameter.AsSpan();
405+
406+
if (templateSpan.StartsWith(parameterSpan, StringComparison.Ordinal))
407+
{
408+
return endpointParameter.Length;
409+
}
410+
411+
const string httpPrefix = "http://";
412+
const string httpsPrefix = "https://";
413+
414+
// Use span based comparison to avoid allocations
415+
if (templateSpan.StartsWith(httpsPrefix.AsSpan(), StringComparison.Ordinal) &&
416+
templateSpan[httpsPrefix.Length..].StartsWith(parameterSpan, StringComparison.Ordinal))
417+
{
418+
return httpsPrefix.Length + endpointParameter.Length;
419+
}
420+
421+
if (templateSpan.StartsWith(httpPrefix.AsSpan(), StringComparison.Ordinal) &&
422+
templateSpan[httpPrefix.Length..].StartsWith(parameterSpan, StringComparison.Ordinal))
423+
{
424+
return httpPrefix.Length + endpointParameter.Length;
425+
}
426+
427+
return 0;
428+
}
429+
396430
private void AddUriSegments(
397431
string segments,
398432
int offset,

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/ClientProviders/ClientProviderTests.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,27 @@ public void EndpointFieldAssignedFromStringParameter(string serverTemplate, stri
999999
StringAssert.Contains($"_endpoint = new global::System.Uri($\"{serverTemplate}\");", constructor?.BodyStatements?.ToDisplayString());
10001000
}
10011001

1002+
[TestCase("{endpoint}", "endpoint")]
1003+
[TestCase("http://{hostName}", "hostName")]
1004+
[TestCase("https://{hostName}", "hostName")]
1005+
public void EndpointAppliedInCreateMethodRequest(string serverTemplate, string parameterName)
1006+
{
1007+
MockHelpers.LoadMockGenerator();
1008+
var client = InputFactory.Client(
1009+
TestClientName,
1010+
methods: [InputFactory.BasicServiceMethod("Foo", InputFactory.Operation("bar", uri: $"{serverTemplate}/foo"))],
1011+
parameters: [InputFactory.Parameter(
1012+
parameterName,
1013+
InputPrimitiveType.String,
1014+
isRequired: true,
1015+
kind: InputParameterKind.Client,
1016+
serverUrlTemplate: serverTemplate,
1017+
isEndpoint: true)]);
1018+
var clientProvider = new ClientProvider(client);
1019+
var createMethod = clientProvider.RestClient.Methods.FirstOrDefault();
1020+
StringAssert.Contains($"uri.Reset(_endpoint);", createMethod?.BodyStatements?.ToDisplayString());
1021+
}
1022+
10021023
private static InputClient GetEnumQueryParamClient()
10031024
{
10041025
return InputFactory.Client(

0 commit comments

Comments
 (0)