MockHttp icon indicating copy to clipboard operation
MockHttp copied to clipboard

Extend fluent API with ability to better match the URI or parts of it

Open skwasjer opened this issue 1 year ago • 0 comments

Currently, we have two request matchers RequestUri() and QueryString(). The former supports wildcards and can be used to effectively ignore portions of a URI, 'and' match the query string as well. The latter has its own specific implementation and if used in combination with RequestUri() requires that the expectation is postfixed with ?*.

This has caused confusion in the past (#25 and #42). The issue with wildcards is that it is very greedy and can match something you don't want. To illustrate once more, */file.jpg matches both http://localhost/file.jpg and http://localhost/some/path/file.jpg. And the fact that RequestUri() and QueryString() don't know about each other does not make things very intuitive. We can fix some of this in documentation, but not all.

Thus, I want to extend the API with additional overloads and pattern matchers to match specific parts of the URI (eg. Scheme, Host, Port, Authority, Path, Query).

Proposal

// Current API with wildcard. Limited but simple to use.
m.RequestUri("https://*/file.jpg?*").QueryString("w", "1024")
// Or
m.RequestUri("https://*/file.jpg?w=*")

// Similar to the current API, but more explicit. The current API would stop supporting wildcards, so you have to opt-in!
// Wildcards have their issues, so more explicit use would be better. Conceivably, we could also introduce glob to address some issues.
m.RequestUri(Pattern.Wildcard("https://*/file.jpg?w=*"))

// Full control via regex (but a bit more difficult and hard to read, however, you do you! ;)).
m.RequestUri(Pattern.Regex(@"^https://[^/]+/file\.jpg\?w=d+$")

// Full control via expressions
m.RequestUri(uri => uri.Scheme == "https" && uri.AbsolutePath == "/file.jpg" && uri.Query.Contains("w=1024"))

// Fluent builder pattern
m.RequestUri(u => u
    .Scheme("https")
    .Path("/file.jpg")
    .Query("w", "1024")
)

// Pattern negation using !
m.RequestUri(!Pattern.Wildcard("http://*"))

// Having all this, we can also combine patterns, expressions and the builder to make it even more flexible.
m.RequestUri(u => u
    .Scheme("https")
    .Path(Pattern.Regex(@"^/file\.jpg$"))
    .Query("w", v => int.TryParse(v, out var w) && w == 1024)
)

All the examples above would match: https://localhost/file.jpg?w=1024 and https://127.0.0.1/file.jpg?w=1024 But not, eg.: http://some-host/file1.jpg?h=2048

Note: We can keep most of the existing API, but the existing QueryString() and WithoutQueryString() most likely would be deprecated as they are moved to the new fluent API builder. This would provide a more consistent API and solve the issues in #25 and #42.

Note2: Parts of this design (specifically the pattern matching) could also be retrofitted into how header matching currently works, thus gaining the ability there also to use regex, expressions, wildcards, etc. in both the header key and header value(s).

skwasjer avatar Sep 08 '24 21:09 skwasjer