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

Add AppendQueryParam(s) methods #689

Merged
merged 2 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@
Assert.AreEqual("http://www.mysite.com/more?x=1&y=2&y=4&y=6&z=3&abc&xyz&foo=&=bar", url.ToString());
}

[Test]
public void can_append_query_params() {
var url = "http://www.mysite.com/more"
.AppendQueryParam("x", 1)

Check failure on line 67 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 67 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 67 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 67 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 67 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
.AppendQueryParam("x", new[] { "2", "4", "6" })
.AppendQueryParam("x", 3)
.AppendQueryParam("x")
.AppendQueryParam("x", "");

Assert.AreEqual("http://www.mysite.com/more?x=1&x=2&x=4&x=6&x=3&x&x=", url.ToString());
}

// #641 (oddly, passing the params object ("" or null) via another TestCase arg didn't repro the bug in the null case)
[TestCase("http://www.mysite.com/more")]
[TestCase("http://www.mysite.com/more?x=1")]
Expand All @@ -81,6 +93,16 @@
Assert.AreEqual("https://api.com?x=1", url.ToString());
}

[Test] // #669
public void can_append_query_params_using_objects_with_nullable_types() {
int? x = 1;
int? y = null;
var query = new { x, y };
var url = new Url("https://api.com");
url.AppendQueryParam(query);
Assert.AreEqual("https://api.com?x=1", url.ToString());
}

#if NET
[Test] // #632
public void can_set_query_params_to_enums_cast_to_ints() {
Expand All @@ -104,6 +126,13 @@
Assert.AreEqual("https://api.com?Values=1&Values=2&Values=3", url.ToString());
}

[Test] // #672
public void can_append_query_params_using_object_with_ienumerable() {
var model = new ModelWithIEnumerable { Values = Enumerable.Range(1, 3).ToArray() };
var url = "https://api.com".AppendQueryParam(model);

Check failure on line 132 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 132 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 132 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 132 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)

Check failure on line 132 in Test/Flurl.Test/UrlBuilder/UrlBuildingTests.cs

View workflow job for this annotation

GitHub Actions / build_test

'string' does not contain a definition for 'AppendQueryParam' and no accessible extension method 'AppendQueryParam' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
Assert.AreEqual("https://api.com?Values=1&Values=2&Values=3", url.ToString());
}

class ModelWithIEnumerable
{
public IEnumerable<int> Values { get; set; }
Expand Down
27 changes: 26 additions & 1 deletion src/Flurl.CodeGen/Metadata.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,31 @@ public static IEnumerable<ExtensionMethod> GetUrlReturningExtensions(MethodArg e
yield return Create("SetQueryParams", "Creates a new Url object from the string and adds multiple parameters without values to the query.")
.AddArg("names", "params string[]", "Names of query parameters");

yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter to the query.")
.AddArg("name", "string", "Name of query parameter")
.AddArg("value", "object", "Value of query parameter")
.AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing)", "NullValueHandling.Remove");

yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter to the query.")
.AddArg("name", "string", "Name of query parameter")
.AddArg("value", "string", "Value of query parameter")
.AddArg("isEncoded", "bool", "Set to true to indicate the value is already URL-encoded. Defaults to false.", "false")
.AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing).", "NullValueHandling.Remove");

yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds a parameter without a value to the query.")
.AddArg("name", "string", "Name of query parameter");

yield return Create("AppendQueryParam", "Creates a new Url object from the string, parses values object into name/value pairs, and adds them to the query, overwriting any that already exist.")
.AddArg("values", "object", "Typically an anonymous object, ie: new { x = 1, y = 2 }")
.AddArg("nullValueHandling", "NullValueHandling", "Indicates how to handle null values. Defaults to Remove (any existing)", "NullValueHandling.Remove");

yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds multiple parameters without values to the query.")
.AddArg("names", "IEnumerable<string>", "Names of query parameters.");

yield return Create("AppendQueryParam", "Creates a new Url object from the string and adds multiple parameters without values to the query.")
.AddArg("names", "params string[]", "Names of query parameters");


yield return Create("RemoveQueryParam", "Creates a new Url object from the string and removes a name/value pair from the query by name.")
.AddArg("name", "string", "Query string parameter name to remove");

Expand Down Expand Up @@ -98,7 +123,7 @@ public static IEnumerable<ExtensionMethod> GetRequestReturningExtensions(MethodA
.AddArg("name", "string", "The cookie name.")
.AddArg("value", "object", "The cookie value.");
yield return Create("WithCookies", "Creates a new FlurlRequest and adds name-value pairs to its Cookie header based on property names/values of the provided object, or keys/values if object is a dictionary. " +
"To automatically maintain a cookie \"session\", consider using a CookieJar or CookieSession instead.")
"To automatically maintain a cookie \"session\", consider using a CookieJar or CookieSession instead.")
.AddArg("values", "object", "Names/values of HTTP cookies to set. Typically an anonymous object or IDictionary.");
yield return Create("WithCookies", "Creates a new FlurlRequest and sets the CookieJar associated with this request, which will be updated with any Set-Cookie headers present in the response and is suitable for reuse in subsequent requests.")
.AddArg("cookieJar", "CookieJar", "The CookieJar.");
Expand Down
73 changes: 73 additions & 0 deletions src/Flurl.Http/UrlBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,79 @@ public static IFlurlRequest SetQueryParams(this IFlurlRequest request, params st
return request;
}

/// <summary>
/// Adds a parameter to the URL query.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="name">Name of query parameter</param>
/// <param name="value">Value of query parameter</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>This IFlurlRequest</returns>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name, object value, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
request.Url.AppendQueryParam(name, value, nullValueHandling);
return request;
}

/// <summary>
/// Adds a parameter to the URL query.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="name">Name of query parameter</param>
/// <param name="value">Value of query parameter</param>
/// <param name="isEncoded">Set to true to indicate the value is already URL-encoded</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>This IFlurlRequest</returns>
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null" />.</exception>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name, string value, bool isEncoded = false, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
request.Url.AppendQueryParam(name, value, isEncoded, nullValueHandling);
return request;
}

/// <summary>
/// Adds a parameter without a value to the URL query.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="name">Name of query parameter</param>
/// <returns>This IFlurlRequest</returns>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, string name) {
request.Url.AppendQueryParam(name);
return request;
}

/// <summary>
/// Parses values (usually an anonymous object or dictionary) into name/value pairs and adds them to the URL query, overwriting any that already exist.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="values">Typically an anonymous object, ie: new { x = 1, y = 2 }</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>This IFlurlRequest</returns>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, object values, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
request.Url.AppendQueryParam(values, nullValueHandling);
return request;
}

/// <summary>
/// Adds multiple parameters without values to the URL query.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="names">Names of query parameters.</param>
/// <returns>This IFlurlRequest</returns>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, IEnumerable<string> names) {
request.Url.AppendQueryParam(names);
return request;
}

/// <summary>
/// Adds multiple parameters without values to the URL query.
/// </summary>
/// <param name="request">The IFlurlRequest associated with the URL</param>
/// <param name="names">Names of query parameters</param>
/// <returns>This IFlurlRequest</returns>
public static IFlurlRequest AppendQueryParam(this IFlurlRequest request, params string[] names) {
request.Url.AppendQueryParam(names as IEnumerable<string>);
return request;
}

/// <summary>
/// Removes a name/value pair from the URL query by name.
/// </summary>
Expand Down
77 changes: 77 additions & 0 deletions src/Flurl/Url.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,83 @@ public Url SetQueryParams(IEnumerable<string> names) {
/// <returns>The Url object with the query parameter added.</returns>
public Url SetQueryParams(params string[] names) => SetQueryParams(names as IEnumerable<string>);

/// <summary>
/// Adds a parameter to the query.
/// </summary>
/// <param name="name">Name of query parameter</param>
/// <param name="value">Value of query parameter</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>The Url object with the query parameter added</returns>
public Url AppendQueryParam(string name, object value, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
QueryParams.Add(name, value, false, nullValueHandling);
return this;
}

/// <summary>
/// Adds a parameter to the query.
/// </summary>
/// <param name="name">Name of query parameter</param>
/// <param name="value">Value of query parameter</param>
/// <param name="isEncoded">Set to true to indicate the value is already URL-encoded</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>The Url object with the query parameter added</returns>
/// <exception cref="ArgumentNullException"><paramref name="name"/> is <see langword="null" />.</exception>
public Url AppendQueryParam(string name, string value, bool isEncoded = false, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
QueryParams.Add(name, value, isEncoded, nullValueHandling);
return this;
}

/// <summary>
/// Adds a parameter without a value to the query.
/// </summary>
/// <param name="name">Name of query parameter</param>
/// <returns>The Url object with the query parameter added</returns>
public Url AppendQueryParam(string name) {
QueryParams.Add(name, null, false, NullValueHandling.NameOnly);
return this;
}

/// <summary>
/// Parses values (usually an anonymous object or dictionary) into name/value pairs and adds them to the query.
/// </summary>
/// <param name="values">Typically an anonymous object, ie: new { x = 1, y = 2 }</param>
/// <param name="nullValueHandling">Indicates how to handle null values. Defaults to Remove (any existing)</param>
/// <returns>The Url object with the query parameters added</returns>
public Url AppendQueryParam(object values, NullValueHandling nullValueHandling = NullValueHandling.Remove) {
if (values == null)
return this;

if (values is string s)
return AppendQueryParam(s);

foreach (var kv in values.ToKeyValuePairs())
AppendQueryParam(kv.Key, kv.Value, nullValueHandling);

return this;
}

/// <summary>
/// Adds multiple parameters without values to the query.
/// </summary>
/// <param name="names">Names of query parameters.</param>
/// <returns>The Url object with the query parameter added</returns>
public Url AppendQueryParam(IEnumerable<string> names) {
if (names == null)
return this;

foreach (var name in names.Where(n => !string.IsNullOrEmpty(n)))
AppendQueryParam(name);

return this;
}

/// <summary>
/// Adds multiple parameters without values to the query.
/// </summary>
/// <param name="names">Names of query parameters</param>
/// <returns>The Url object with the query parameter added.</returns>
public Url AppendQueryParam(params string[] names) => SetQueryParams(names as IEnumerable<string>);

/// <summary>
/// Removes a name/value pair from the query by name.
/// </summary>
Expand Down