Skip to content

Commit

Permalink
Merge pull request #689 from Marusyk/rmarusyk/688
Browse files Browse the repository at this point in the history
Add AppendQueryParam(s) methods
  • Loading branch information
tmenier authored Sep 15, 2023
2 parents a3df496 + b941ecc commit b5768a9
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 1 deletion.
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 @@ public void can_set_query_params() {
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 @@ public void can_set_query_params_using_objects_with_nullable_types() {
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 @@ public void can_set_query_params_using_object_with_ienumerable() {
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

0 comments on commit b5768a9

Please sign in to comment.