kiota icon indicating copy to clipboard operation
kiota copied to clipboard

Trailing slash in URL is dropped during generation

Open mderriey opened this issue 1 year ago • 11 comments

Hi there

Problem

We're working with an API which endpoints contain trailing slashes, for example {+baseUrl}/api/v1/app/{app_id}/msg/.

During the client generation, the trailing slash is dropped, and the generated client doesn't work as expected as all requests return a 404.

Repro

For reference, here's a link to the OpenAPI document used here: https://github.com/svix/svix-webhooks/blob/8d32e47e0484f5d0839bce364d8700d2c7457937/openapi.json#L7779

  1. Generate the .NET client
    kiota generate `
      --openapi https://raw.githubusercontent.com/svix/svix-webhooks/main/openapi.json `
      --output .\TrailingSlashDroppedIssue4291 `
      --language CSharp `
      --class-name SvixClient `
      --namespace-name SvixApiClient `
      --exclude-backward-compatible true `
      --serializer Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory `
      --deserializer Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory `
      --structured-mime-types application/json `
      --include-path '/api/v1/app/{app_id}/msg/#POST'
    
  2. See the missing trailing slashes in the constructor definitions in the TrailingSlashDroppedIssue4291\Api\V1\App\Item\Msg\MsgRequestBuilder.cs file
    public class MsgRequestBuilder : BaseRequestBuilder {
        public MsgRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/v1/app/{app_id}/msg{?with_content*}", pathParameters) {
        }
        public MsgRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/api/v1/app/{app_id}/msg{?with_content*}", rawUrl) {
        }
    }
    

Expected result

Kiota shouldn't manipulate paths defined in the OpenAPI document, or should provide an option to opt out of this behavior.

mderriey avatar Mar 05 '24 13:03 mderriey

Hi @mderriey, Thanks for using kiota and for reaching out. Let me start by saying that while requiring trailing slashes is supported according to RFCs 9110 and 3986, it's a bit unusual from a REST API design perspective, see this stack overflow post. This is not an excuse, just adding some perspective here.

Kiota generates the URI templates here and additional unit tests can be added here

The challenge is that kiota relies on OpenAPI.net to parse the description and build the tree of the API. And that process kind of relies on trimming the trailing slashes to work as you can see here and there. In fact it's removing the trailing slash

From kiota's perspective, the trailing slash doesn't exist, and adding it systematically would be wrong in the same sense it's wrong to remove it.

What needs to happen it:

  1. OpenAPI.net needs to updated to keep the trailing slash without derailing the control logic
  2. Once the patch is released and pulled in kiota, the slash should appear "magically"

Would you be willing to work on a pull request for openapi.net?

baywet avatar Mar 05 '24 18:03 baywet

Hi @baywet

Thanks a lot for the detailed explanation.

I couldn't find the exact place where OpenAPI.NET removes trailing slashes — one of the links you pointed to is to remove the leading slash, not the trailing one.

I did a quick test and used OpenAPI.NET to get a representation of the Svix OpenAPI document, and the paths do have the trailing slashes.

Click to expand code sample and output

Code

internal class Program
{
    static async Task Main(string[] args)
    {
        var httpClient = new HttpClient
        {
            BaseAddress = new Uri("https://raw.githubusercontent.com/svix/svix-webhooks/")
        };

        var stream = await httpClient.GetStreamAsync("main/openapi.json");

        // Read V3 as YAML
        var openApiDocument = new OpenApiStreamReader().Read(stream, out var _);

        // Look for our endpoint
        var messagePath = openApiDocument.Paths.FirstOrDefault(x => x.Key == "/api/v1/app/{app_id}/msg/");
        if (messagePath.Equals(default(KeyValuePair<string, OpenApiPathItem>)))
        {
            Console.WriteLine("No path with the trailing slash");
        }
        else
        {
            Console.WriteLine("Yes path with the trailing slash");
        }
    }
}

Output

Yes path with the trailing slash

This is using Microsoft.OpenApi.Readers v1.6.13, the same one Kiota uses today.

Does this indicate the issue does lie in Kiota, or does Kiota parse the OpenAPI document in a different way / with different settings that could make OpenAPI.NET strip trailing slashes?

mderriey avatar Mar 06 '24 09:03 mderriey

My bad, please ignore my last message, after writing a test in the Kiota codebase, I can see now how it comes from OpenApiUrlTreeNode, which you originally pointed to. And I also realised what I thought was the leading slash is actually the trailing slash as it works segment by segment.

Let me open an issue in the OpenAPI.NET repo, thanks again.

mderriey avatar Mar 06 '24 09:03 mderriey