-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support trailing slash in URL paths #5651
base: main
Are you sure you want to change the base?
Support trailing slash in URL paths #5651
Conversation
The typescript writer has a conflict when there is more than 1 code file in a single namespace.
47fea19
to
4373e58
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the contribution! And for the great description here.
I do think understanding the problem better will help us design a less intrusive solution.
Can we start by identifying patterns where the trailing slash in the URI template is problematic?
e.g. if we only have /foo/bar/
(instead of /foo/bar
) in the description/selected operations, this is not and issue, we can simply project the trailing slash in the uri template, using the same conventions for the request builders and whatnot.
if we have both /foo/bar/
and /foo/{id}
what happens? what about /foo/
and /foo
? Any other pattern we should be careful about?
Some extremely informative information on the topic
@@ -95,6 +95,7 @@ public CodeDocumentation Documentation | |||
Name = $"{codeClass.Name.ToFirstCharacterLowerCase()}{RequestsMetadataSuffix}", | |||
Kind = CodeConstantKind.RequestsMetadata, | |||
OriginalCodeElement = codeClass, | |||
UriTemplate = $"{codeClass.Name.ToFirstCharacterLowerCase()}{UriTemplateSuffix}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are we using the class name as uri template here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mentioned it briefly in the PR description but part of the fix in the TypeScript refiner is merging multiple CodeFiles into one CodeFile.
The consequence is that a CodeFile now has multiple UriTemplates in it.
When the TypeScript writer finally gets to writing the request builder's RequestsMetadata constant, it previously only selects the first UriTemplate even if it is the wrong one.
This commit sets UriTemplate in the RequestMetadata CodeConstant so that the TypeScript writer can use it to look up the correct UriTemplate.
@@ -266,6 +266,11 @@ private static string NormalizeSymbolsBeforeCleanup(string original) | |||
result = result.Replace("+", "_plus_", StringComparison.OrdinalIgnoreCase); | |||
} | |||
|
|||
if (result.Contains('\\', StringComparison.OrdinalIgnoreCase)) | |||
{ | |||
result = result.Replace(@"\", "Slash", StringComparison.OrdinalIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from reading the unit tests this effectively inserts an additional suffix in the request builder's name.
this solution looks disruptive to me, but I'll follow up in the main comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I completely agree and look forward to whatever alternative we come up with!
Thanks for the feedback!
For a minimal spec containing
For a minimal spec containing
export const FooRequestBuilderRequestsMetadata: RequestsMetadata = {
get: {
uriTemplate: FooRequestBuilderUriTemplate,
responseBodyContentType: "text/plain;q=0.9",
adapterMethodName: "sendPrimitive",
responseBodyFactory: "string",
},
get: {
uriTemplate: FooRequestBuilderUriTemplate,
responseBodyContentType: "text/plain;q=0.9",
adapterMethodName: "sendPrimitive",
responseBodyFactory: "string",
},
};
The patterns I specifically focused on for this PR are in this sample test file: tests/Kiota.Builder.Tests/OpenApiSampleFiles/TrailingSlashSampleYml.cs However, as you brought up, there are more!
I have started this and it turns out that there are some edge cases I had not considered. I'll post another comment once I have a report for you, likely in the form of a GitHub repo so we can browse and discuss differences and desired state between the generated results. I've put this PR back into a draft state for now. Footnotes |
Thanks for the detailed answer here! I think this and your next answer will help flesh things out to an improved solution. |
@chelkyl gentle nudge on the topic :) |
Thanks for the poke! Please see the report here. Highlights:
|
Thank you for this really detailed report! I think one thing is being worrisome is the difference between languages. The flakiness (collision based on ordering), I'd have expected BEFORE the "Slash" convention in the naming was added. I think the first goal should be to get rid of flaky results since then generated code for each language depends on this step. Looking at the DOM before refiners are applied would probably help abstract for language changes. Let us know if you have any additional comments or questions. And thanks for the hard work here! |
Fixes #4291
Nearly all language-specific writers support parameterized paths that have a trailing slash. The TypeScript writer does correctly write the request builder files but because it writes to
index.ts
rather than to a filename based on the request builder like the other writers, there is a conflict when a code namespace has more than one code file.When the spec has paths like
/{foo}
and/{foo}/
, kiota writes the first request builder correctly then overwrites it with the second request builder.The first commit addresses this in the TypeScript refiner by detecting when the code namespace has more than one code file and merging them all into one.
However, this creates a new problem because the request metadata generator only selects the first UriTemplate. With the code files merged, there are multiple UriTemplate elements so one of the request metadata constants would have the wrong UriTemplate.
The second commit addresses this by taking advantage of how the UriTemplate property is unused in a CodeConstantKind.RequestsMetadata code constant and sets it to match the expected name of the correct UriTemplate. The request metadata generator can then use this now-populated property to find the correct UriTemplate.
Unfortunately, this creates a hidden coupling with how UriTemplates are named but it appears to be unavoidable.
Like the first issue, non-parameterized paths that have a trailing slash also result in conflicts. However, the conflict is earlier in the process so it affects all languages.
When the spec has paths like
/foo
and/foo/
, kiota resolves both nodes (segmentsfoo
andfoo\\
respectively) to the same name which causes a lot of problems later, resulting in broken generated code.The third commit addresses this by changing how slashes are cleaned up so that the
foo
segment still results infoo
but thefoo\\
segment results infooSlash
. This also changes the naming of code elements likefoo\\RequestBuilder
fromfooRequestBuilder
tofooSlashRequestBuilder
.I'm not completely happy with this third commit because the trailing slash will now leak into the code element names and files. However, with my shallow understanding of the project, I'm not seeing a better way without messier changes.