Skip to content
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

#2199 Adding support of placeholder matching between slashes👍 #2200

Merged

Conversation

ggnaegi
Copy link
Member

@ggnaegi ggnaegi commented Nov 12, 2024

Fixes #2199

Proposed Changes

  • Reworked UrlPathPlaceholderNameAndValueFinder.cs supporting now embedded placeholders

  • Pattern matching with Regexes

    Regex regex = new($@"\{PlaceHolderLeft}(.*?)\{PlaceHolderRight}", RegexOptions.None, TimeSpan.FromSeconds(1));
    template = EscapeExceptBraces(template);
    string regexPattern = $"^{regex.Replace(template, match => $"(?<{match.Groups[1].Value}>[^&]*)")}";
  • Some tweaks:

    • If the path template contains a catch-all query, then return the catch-all placeholder with an empty value
    private static (bool IsMatch, string Placeholder) IsCatchAllQuery(string template)
        {
            Regex catchAllQueryRegex = new($@"^[^{{}}]*\?\{PlaceHolderLeft}(.*?)\{PlaceHolderRight}$", RegexOptions.None, TimeSpan.FromMilliseconds(300));
            Match catchAllMatch = catchAllQueryRegex.Match(template);
    
            return (catchAllMatch.Success, catchAllMatch.Success ? catchAllMatch.Groups[1].Value : string.Empty);
        }
    • If the path template contains a catch all path, then return the catch-all placeholder with an empty value
    • Skipping the query evaluation if not present in the path template
  • Added some unit tests with complex path templates.

  • Added code comments

@ggnaegi ggnaegi added the Routing Ocelot feature: Routing label Nov 12, 2024
@ggnaegi ggnaegi requested review from RaynaldM and raman-m November 12, 2024 22:06
@ggnaegi ggnaegi self-assigned this Nov 12, 2024
…oduct/products/categories/ since the last slash is part of the template and not the catch-all placeholder
@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 12, 2024

@raman-m yeaaaah, the checks have passed ;-)

@raman-m
Copy link
Member

raman-m commented Nov 13, 2024

Gui, how quickly do you need this delivered? Do you require the officially released NuGet package, or can you wait for the next v24.0?

@raman-m
Copy link
Member

raman-m commented Nov 13, 2024

yeaaaah, the checks have passed ;-)

Congratulations! 🎉 It appears that the old functionality remains intact.

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 13, 2024

Gui, how quickly do you need this delivered? Do you require the officially released NuGet package, or can you wait for the next v24.0?

It's urgent, but, when it is foreseen to release v24.0?

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestions are below 👇

@raman-m
Copy link
Member

raman-m commented Nov 13, 2024

It's urgent, but, when it is foreseen to release v24.0?

@ggnaegi, I'm OK to release minor version 23.4.0 with the already merged 2 features + this feat. Will you prepare the release PR for urgent delivery? Will you write the release notes, etc.? Or will our next minor release be without release notes? 😄

when it is foreseen to release v24.0?

First days, 1st week of December, I guess...

@raman-m raman-m changed the title Adding support for placeholder matching between slashes. #2199 Adding support for placeholder matching between slashes, in the middle of URL path segment Nov 13, 2024
@raman-m raman-m added bug Identified as a potential bug proposal Proposal for a new functionality in Ocelot high High priority labels Nov 13, 2024
@raman-m raman-m added this to the Autumn'24 milestone Nov 13, 2024
@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 13, 2024

It's urgent, but, when it is foreseen to release v24.0?

@ggnaegi, I'm OK to release minor version 23.4.0 with the already merged 2 features + this feat. Will you prepare the release PR for urgent delivery? Will you write the release notes, etc.? Or will our next minor release be without release notes? 😄

when it is foreseen to release v24.0?

First days, 1st week of December, I guess...

Ok let's do this, minor version 23.4.0

@raman-m
Copy link
Member

raman-m commented Nov 13, 2024

Ok let's do this, minor version 23.4.0

OK! Your destiny, Gui, is to write the documentation for the feature. It seems we have to review all Routing documentation to inform the community that we support placeholders in the middle.
As you know, the documentation now includes all placeholder examples "between slashes".

@raman-m
Copy link
Member

raman-m commented Nov 14, 2024

@ggnaegi Are you going to write acceptance tests and update docs?

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 14, 2024

@ggnaegi Are you going to write acceptance tests and update docs?

Yes!

…aceholderNameAndValueFinderTests.cs

Recover old test for feat 89.
Remove BDDfy from new unit tests.
Apply AAA-pattern.
@raman-m
Copy link
Member

raman-m commented Nov 14, 2024

The build failed because of unsupported can_match_down_stream_url_with_downstream_template_with_place_holder_to_final_url_path test❗
So, the old feat #89 is broken after refactroring❗
We must support old functionality, at least try to support...

I've recovered the old unit test

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 14, 2024

The build failed because of unsupported can_match_down_stream_url_with_downstream_template_with_place_holder_to_final_url_path test❗ So, the old feat #89 is broken after refactroring❗ We must support old functionality, at least try to support...

I've recovered the old unit test

Yes, but the old unit test, is totally crap! I'm wondering why it should pass! It's stupid

@raman-m
Copy link
Member

raman-m commented Nov 14, 2024

Yes, but the old unit test, is totally crap! I'm wondering why it should pass! It's stupid

Do you think we had a stupid contributor in #89 ? 😄

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 14, 2024

Yes, but the old unit test, is totally crap! I'm wondering why it should pass! It's stupid

Do you think we had a stupid contributor in #89 ? 😄

this.Given(x => x.GivenIHaveAUpstreamPath("api/product/products/categories/"))
                 .And(x => x.GivenIHaveAnUpstreamUrlTemplate("api/{finalUrlPath}/")) // Gui, don't remove final slash! We should not break old functionality
                 .When(x => x.WhenIFindTheUrlVariableNamesAndValues())
                 .And(x => x.ThenTheTemplatesVariablesAre(expectedTemplates))
                 .BDDfy();
        }

Why on earth {finalUrlPath} should match /product/products/categories/ if the slash is not part of it?????

Besides, the acceptance test is passing...
should_return_response_200_with_placeholder_for_final_url_path()

@raman-m

Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I require the following missing artifacts from the development process:

  • Acceptance test(s) with the same data as the new unit tests
  • Updated documentation for the Routing feature, at least draft with ideas to review

P.S. Regarding the #89 failed test

I saw that the unit test fails, but the corresponding #89 acceptance test doesn't, which is strange. Logically, both tests should fail together or they should both pass. I recommend carefully reviewing the #89 and syncing the test data, at least for a final decision.

@ThreeMammals ThreeMammals deleted a comment from ggnaegi Nov 15, 2024
@raman-m
Copy link
Member

raman-m commented Nov 15, 2024

Upon reviewing the old #89 tests, I noticed that the author made a typo error in the unit test, specifically an extra / on line 152, in the first commit that only included the unit test. Subsequently, Tom requested an acceptance test, which lacked a final slash in the test data. To the best of my knowledge, the acceptance test aligns with the requirements described in #89, but the unit test does not. It appears to be a mistyping by the author.

The tests have been reviewed and are now successfully passing. Apologies for any inconvenience over the past two days!
FYI, the author implemented a fix in the code, PR line 47, which reads urlPathTemplate.Trim('/').EndsWith(variableName). Currently, this fix for the unit test is located at this line:

if (positionOfNextSlash == -1 || (urlPathTemplate.Trim(delimiter).EndsWith(variableName) && string.IsNullOrEmpty(query)))
, where the slash trimming is still presented as Trim(delimiter). This is why the old typo and the corrected unit test coexist in the codebase. However, this does not align with the actual requirements of feature #89.

If you don't mind, I will refactor both files along with the tests to adhere to our development conventions.
New tests won't change...

@raman-m
Copy link
Member

raman-m commented Nov 16, 2024

@ggnaegi Are you going to write acceptance tests and update docs?

Yes!

When?

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 16, 2024

@raman-m 🤣

@ggnaegi
Copy link
Member Author

ggnaegi commented Nov 16, 2024

@raman-m ready

@ggnaegi ggnaegi changed the title #2199 Adding support for placeholder matching between slashes, in the middle of URL path segment #2199 Adding support of placeholder matching between slashes. Nov 16, 2024
@raman-m raman-m changed the title #2199 Adding support of placeholder matching between slashes. #2199 Adding support of placeholder matching between slashes Nov 17, 2024
@ggnaegi ggnaegi changed the title #2199 Adding support of placeholder matching between slashes #2199 Adding support of placeholder matching between slashes; Nov 17, 2024
FindGroups method is private, better to return exact type.
Regex optimizations across net6, 7, 8.
XML dev-docs markup review.
Rename 'curly bracket' to 'brace'.
Copy link
Member

@raman-m raman-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ready for Delivery ✅

Feel free to press the magic Squash button! 🎉
If you merge today, I will prepare the release v23.4.0 for tomorrow.

Comment on lines +76 to +77
Embedded Placeholders [#f1]_
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay now. Thank you for writing this! I will review the section again during the release.

Comment on lines +411 to +430
[Theory]
[Trait("Bug", "2199")]
[Trait("Feat", "2200")]
[InlineData("/api/invoices/{url0}-{url1}-{url2}", "/api/invoices_{url0}/{url1}-{url2}_abcd/{url3}?urlId={url4}",
"/api/invoices_abc/def-ghi_abcd/xyz?urlId=bla", "/api/invoices/abc-def-ghi", "?urlId=bla")]
[InlineData("/api/products/{category}-{subcategory}/{filter}", "/api/products_{category}/{subcategory}_details/{itemId}?filter={filter}",
"/api/products_electronics/computers_details/123?filter=active", "/api/products/electronics-computers/active", "")]
[InlineData("/api/users/{userId}/posts/{postId}/{lang}", "/api/users/{userId}/{postId}_content/{timestamp}?lang={lang}",
"/api/users/101/2022_content/2024?lang=en", "/api/users/101/posts/2022/en", "")]
[InlineData("/api/categories/{cat}-{subcat}?sort={sort}", "/api/categories_{cat}/{subcat}_items/{itemId}?sort={sort}",
"/api/categories_home/furniture_items/789?sort=asc", "/api/categories/home-furniture", "?sort=asc")]
[InlineData("/api/orders/{order}-{detail}?status={status}", "/api/orders_{order}/{detail}_invoice/{ref}?status={status}",
"/api/orders_987/abc_invoice/123?status=shipped", "/api/orders/987-abc", "?status=shipped")]
[InlineData("/api/transactions/{type}-{region}", "/api/transactions_{type}/{region}_summary/{year}?q={query}",
"/api/transactions_sales/NA_summary/2024?q=forecast", "/api/transactions/sales-NA", "?q=forecast")]
[InlineData("/api/resources/{resource}-{subresource}", "/api/resources_{resource}/{subresource}_data/{id}?key={apikey}",
"/api/resources_images/photos_data/555?key=xyz123", "/api/resources/images-photos", "?key=xyz123")]
[InlineData("/api/accounts/{account}-{detail}", "/api/accounts_{account}/{detail}_info/{id}?opt={option}",
"/api/accounts_admin/settings_info/101?opt=true", "/api/accounts/admin-settings", "?opt=true")]
public void ShouldMatchComplexQueriesWithEmbeddedPlaceholders(string downstream, string upstream, string requestUrl, string downstreamPath, string queryString)
Copy link
Member

@raman-m raman-m Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding these tests! I have a question.
Does the feat support embedded placeholders within a single query parameter and its value?
For example →

  • /api/invoices_{url0}/{url1}-{url2}_abcd/{url3}?Id1={id1}&filter=A{fid1}B&text=quick fox jumped over lazy {animal2} and&etc

I assume the answer is NO...

Copy link
Member Author

@ggnaegi ggnaegi Nov 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@raman-m It should work yes, it focuses on the left and right brackets for the matching

@raman-m raman-m changed the title #2199 Adding support of placeholder matching between slashes; #2199 Adding support of placeholder matching between slashes👍 Nov 17, 2024
@raman-m raman-m added the Dec'24 December 2024 release label Nov 17, 2024
@ggnaegi ggnaegi merged commit 27d3df2 into ThreeMammals:develop Nov 17, 2024
1 check passed
@raman-m raman-m added Nov'24 November 2024 release and removed Dec'24 December 2024 release labels Nov 20, 2024
@raman-m raman-m modified the milestones: Autumn'24, November'24 Nov 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Identified as a potential bug high High priority Nov'24 November 2024 release proposal Proposal for a new functionality in Ocelot Routing Ocelot feature: Routing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ocelot can't match placeholders between slashes
2 participants