Skip to content

Cleaning up parameters #1683

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

Merged
merged 1 commit into from
Jan 7, 2022
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
9 changes: 5 additions & 4 deletions src/RestSharp/Extensions/MiscExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public static async Task<byte[]> ReadAsBytes(this Stream input, CancellationToke

return ms.ToArray();
}
internal static IEnumerable<(string Name, object Value)> GetProperties(this object obj, params string[] includedProperties) {

internal static IEnumerable<(string Name, string? Value)> GetProperties(this object obj, params string[] includedProperties) {
// automatically create parameters from object props
var type = obj.GetType();
var props = type.GetProperties();
Expand All @@ -63,12 +63,13 @@ public static async Task<byte[]> ReadAsBytes(this Stream input, CancellationToke
if (array.Length > 0 && elementType != null) {
// convert the array to an array of strings
var values = array.Cast<object>().Select(item => item.ToString());
yield return (prop.Name, string.Join(",", values));

val = string.Join(",", values);
continue;
}
}

yield return(prop.Name, val);
yield return (prop.Name, val.ToString());
}

bool IsAllowedProperty(string propertyName)
Expand Down
1 change: 1 addition & 0 deletions src/RestSharp/KnownHeaders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ public static class KnownHeaders {
public const string ContentType = "Content-Type";
public const string LastModified = "Last-Modified";
public const string ContentMD5 = "Content-MD5";
public const string Host = "Host";
}
15 changes: 4 additions & 11 deletions src/RestSharp/Parameters/FileParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ namespace RestSharp;
/// </summary>
[PublicAPI]
public record FileParameter {
/// <summary>
/// The length of data to be sent
/// </summary>
public long ContentLength { get; }

/// <summary>
/// Provides raw data for file
/// </summary>
Expand All @@ -44,10 +39,9 @@ public record FileParameter {
/// </summary>
public string Name { get; }

FileParameter(string name, string fileName, long contentLength, Func<Stream> getFile, string? contentType = null) {
FileParameter(string name, string fileName, Func<Stream> getFile, string? contentType = null) {
Name = name;
FileName = fileName;
ContentLength = contentLength;
GetFile = getFile;
ContentType = contentType ?? "application/octet-stream";
}
Expand All @@ -61,7 +55,7 @@ public record FileParameter {
/// <param name="contentType">The content type to use in the request.</param>
/// <returns>The <see cref="FileParameter" /></returns>
public static FileParameter Create(string name, byte[] data, string filename, string? contentType = null) {
return new FileParameter(name, filename, data.Length, GetFile, contentType);
return new FileParameter(name, filename, GetFile, contentType);

Stream GetFile() {
var stream = new MemoryStream();
Expand All @@ -86,17 +80,16 @@ public static FileParameter Create(
string fileName,
string? contentType = null
)
=> new(name, fileName, contentLength, getFile, contentType ?? Serializers.ContentType.File);
=> new(name, fileName, getFile, contentType ?? Serializers.ContentType.File);

public static FileParameter FromFile(string fullPath, string? name = null, string? contentType = null) {
if (!File.Exists(Ensure.NotEmptyString(fullPath, nameof(fullPath))))
throw new FileNotFoundException("File not found", fullPath);

var fileName = Path.GetFileName(fullPath);
var parameterName = name ?? fileName;
var length = new FileInfo(fullPath).Length;

return new FileParameter(parameterName, fileName, length, GetFile);
return new FileParameter(parameterName, fileName, GetFile);

Stream GetFile() => File.OpenRead(fullPath);
}
Expand Down
7 changes: 6 additions & 1 deletion src/RestSharp/Parameters/GetOrPostParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

namespace RestSharp;

public record GetOrPostParameter : NamedParameter {
/// <summary>
/// Instantiates an HTTP parameter instance (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
/// </summary>
/// <param name="name">Name of the parameter</param>
/// <param name="value">Value of the parameter</param>
/// <param name="encode">Encode the value or not, default true</param>
public GetOrPostParameter(string name, string? value, bool encode = true) : base(name, value, ParameterType.GetOrPost, encode) { }
}
5 changes: 5 additions & 0 deletions src/RestSharp/Parameters/HeaderParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,10 @@
namespace RestSharp;

public record HeaderParameter : Parameter {
/// <summary>
/// Instantiates a header parameter
/// </summary>
/// <param name="name">Parameter name</param>
/// <param name="value">Parameter value</param>
public HeaderParameter(string? name, string? value) : base(name, value, ParameterType.HttpHeader, false) { }
}
6 changes: 6 additions & 0 deletions src/RestSharp/Parameters/QueryParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,11 @@
namespace RestSharp;

public record QueryParameter : NamedParameter {
/// <summary>
/// Instantiates a new query parameter instance that will be added to the request URL as {name}={value} part of the query string.
/// </summary>
/// <param name="name">Parameter name</param>
/// <param name="value">Parameter value</param>
/// <param name="encode">Optional: encode the value, default is true</param>
public QueryParameter(string name, string? value, bool encode = true) : base(name, value, ParameterType.QueryString, encode) { }
}
8 changes: 7 additions & 1 deletion src/RestSharp/Parameters/UrlSegmentParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,17 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

namespace RestSharp;

public record UrlSegmentParameter : NamedParameter {
/// <summary>
/// Instantiates a new query parameter instance that will be added to the request URL part of the query string.
/// The request resource should have a placeholder {name} that will be replaced with the parameter value when the request is made.
/// </summary>
/// <param name="name">Parameter name</param>
/// <param name="value">Parameter value</param>
/// <param name="encode">Optional: encode the value, default is true</param>
public UrlSegmentParameter(string name, string value, bool encode = true)
: base(name, Ensure.NotEmpty(value, nameof(value)).Replace("%2F", "/").Replace("%2f", "/"), ParameterType.UrlSegment, encode) { }
}
13 changes: 11 additions & 2 deletions src/RestSharp/Request/RestRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,18 @@ public Func<HttpResponseMessage, RestResponse>? AdvancedResponseWriter {
}
}

public RestRequest AddParameter(Parameter p) => this.With(x => x.Parameters.AddParameter(p));
/// <summary>
/// Adds a parameter object to the request parameters
/// </summary>
/// <param name="parameter">Parameter to add</param>
/// <returns></returns>
public RestRequest AddParameter(Parameter parameter) => this.With(x => x.Parameters.AddParameter(parameter));

public void RemoveParameter(Parameter p) => Parameters.RemoveParameter(p);
/// <summary>
/// Removes a parameter object from the request parameters
/// </summary>
/// <param name="parameter">Parameter to remove</param>
public void RemoveParameter(Parameter parameter) => Parameters.RemoveParameter(parameter);

internal RestRequest AddFile(FileParameter file) => this.With(x => x._files.Add(file));
}
96 changes: 69 additions & 27 deletions src/RestSharp/Request/RestRequestExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,49 +30,75 @@ public static class RestRequestExtensions {
/// <param name="value">Value of the parameter</param>
/// <param name="encode">Encode the value or not, default true</param>
/// <returns>This request</returns>
public static RestRequest AddParameter(this RestRequest request, string name, object? value, bool encode = true)
=> request.AddParameter(new GetOrPostParameter(name, value?.ToString(), encode));
public static RestRequest AddParameter(this RestRequest request, string name, string? value, bool encode = true)
=> request.AddParameter(new GetOrPostParameter(name, value, encode));

public static RestRequest AddParameter(this RestRequest request, string? name, object value, ParameterType type, bool encode = true)
=> request.AddParameter(Parameter.CreateParameter(name, value, type, encode));

public static RestRequest AddOrUpdateParameter(this RestRequest request, Parameter parameter) {
var p = request.Parameters.FirstOrDefault(x => x.Name == parameter.Name && x.Type == parameter.Type);
/// <summary>
/// Adds a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
/// </summary>
/// <param name="request">Request instance</param>
/// <param name="name">Name of the parameter</param>
/// <param name="value">Value of the parameter</param>
/// <param name="encode">Encode the value or not, default true</param>
/// <returns>This request</returns>
public static RestRequest AddParameter<T>(this RestRequest request, string name, T value, bool encode = true) where T : struct
=> request.AddParameter(name, value.ToString(), encode);

if (p != null) request.RemoveParameter(p);
/// <summary>
/// Adds or updates a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
/// </summary>
/// <param name="request">Request instance</param>
/// <param name="name">Name of the parameter</param>
/// <param name="value">Value of the parameter</param>
/// <param name="encode">Encode the value or not, default true</param>
/// <returns>This request</returns>
public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, string? value, bool encode = true)
=> request.AddOrUpdateParameter(new GetOrPostParameter(name, value, encode));

request.AddParameter(parameter);
return request;
}
/// <summary>
/// Adds or updates a HTTP parameter to the request (QueryString for GET, DELETE, OPTIONS and HEAD; Encoded form for POST and PUT)
/// </summary>
/// <param name="request">Request instance</param>
/// <param name="name">Name of the parameter</param>
/// <param name="value">Value of the parameter</param>
/// <param name="encode">Encode the value or not, default true</param>
/// <returns>This request</returns>
public static RestRequest AddOrUpdateParameter<T>(this RestRequest request, string name, T value, bool encode = true) where T : struct
=> request.AddOrUpdateParameter(name, value.ToString(), encode);

public static RestRequest AddOrUpdateParameters(this RestRequest request, IEnumerable<Parameter> parameters) {
foreach (var parameter in parameters)
request.AddOrUpdateParameter(parameter);
public static RestRequest AddUrlSegment(this RestRequest request, string name, string value, bool encode = true)
=> request.AddParameter(new UrlSegmentParameter(name, value, encode));

return request;
}
public static RestRequest AddUrlSegment<T>(this RestRequest request, string name, T value, bool encode = true) where T : struct
=> request.AddUrlSegment(name, Ensure.NotNull(value.ToString(), nameof(value)), encode);

public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, object? value)
=> request.AddOrUpdateParameter(new GetOrPostParameter(name, value?.ToString()));
public static RestRequest AddQueryParameter(this RestRequest request, string name, string? value, bool encode = true)
=> request.AddParameter(new QueryParameter(name, value, encode));

public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, object value, ParameterType type, bool encode = true)
=> request.AddOrUpdateParameter(Parameter.CreateParameter(name, value, type, encode));
public static RestRequest AddQueryParameter<T>(this RestRequest request, string name, T value, bool encode = true) where T : struct
=> request.AddQueryParameter(name, value.ToString(), encode);

public static RestRequest AddHeader(this RestRequest request, string name, string value) {
CheckAndThrowsForInvalidHost(name, value);
return request.AddParameter(new HeaderParameter(name, value));
}

public static RestRequest AddHeader<T>(this RestRequest request, string name, T value) where T : struct
=> request.AddHeader(name, Ensure.NotNull(value.ToString(), nameof(value)));

public static RestRequest AddOrUpdateHeader(this RestRequest request, string name, string value) {
CheckAndThrowsForInvalidHost(name, value);
return request.AddOrUpdateParameter(new HeaderParameter(name, value));
}

public static RestRequest AddOrUpdateHeader<T>(this RestRequest request, string name, T value) where T : struct
=> request.AddOrUpdateHeader(name, Ensure.NotNull(value.ToString(), nameof(value)));

public static RestRequest AddHeaders(this RestRequest request, ICollection<KeyValuePair<string, string>> headers) {
CheckAndThrowsDuplicateKeys(headers);

foreach (var pair in headers) {
request.AddHeader(pair.Key, pair.Value);
foreach (var header in headers) {
request.AddHeader(header.Key, header.Value);
}

return request;
Expand All @@ -88,11 +114,27 @@ public static RestRequest AddOrUpdateHeaders(this RestRequest request, ICollecti
return request;
}

public static RestRequest AddUrlSegment(this RestRequest request, string name, string value, bool encode = true)
=> request.AddParameter(new UrlSegmentParameter(name, value, encode));
public static RestRequest AddParameter(this RestRequest request, string? name, object value, ParameterType type, bool encode = true)
=> request.AddParameter(Parameter.CreateParameter(name, value, type, encode));

public static RestRequest AddQueryParameter(this RestRequest request, string name, string value, bool encode = true)
=> request.AddParameter(new QueryParameter(name, value, encode));
public static RestRequest AddOrUpdateParameter(this RestRequest request, Parameter parameter) {
var p = request.Parameters.FirstOrDefault(x => x.Name == parameter.Name && x.Type == parameter.Type);

if (p != null) request.RemoveParameter(p);

request.AddParameter(parameter);
return request;
}

public static RestRequest AddOrUpdateParameters(this RestRequest request, IEnumerable<Parameter> parameters) {
foreach (var parameter in parameters)
request.AddOrUpdateParameter(parameter);

return request;
}

public static RestRequest AddOrUpdateParameter(this RestRequest request, string name, object value, ParameterType type, bool encode = true)
=> request.AddOrUpdateParameter(Parameter.CreateParameter(name, value, type, encode));

/// <summary>
/// Adds a file parameter to the request body. The file will be read from disk as a stream.
Expand Down Expand Up @@ -198,7 +240,7 @@ public static RestRequest AddObject(this RestRequest request, object obj, params
static void CheckAndThrowsForInvalidHost(string name, string value) {
static bool InvalidHost(string host) => Uri.CheckHostName(PortSplitRegex.Split(host)[0]) == UriHostNameType.Unknown;

if (name == "Host" && InvalidHost(value))
if (name == KnownHeaders.Host && InvalidHost(value))
throw new ArgumentException("The specified value is not a valid Host header string.", nameof(value));
}

Expand Down
4 changes: 2 additions & 2 deletions src/RestSharp/RestClientExtensions.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public static partial class RestClientExtensions {
var param = $"{name}";

if (resource.Contains(param)) {
resource = resource.Replace(param, value.ToString());
resource = resource.Replace(param, value);
}
else {
query.Add(new QueryParameter(name, value?.ToString()));
query.Add(new QueryParameter(name, value));
}
}

Expand Down
13 changes: 12 additions & 1 deletion test/RestSharp.Tests/ParametersTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,21 @@ public void AddDefaultHeadersUsingDictionary() {

var expected = headers.Select(x => new HeaderParameter(x.Key, x.Value));

var client = new RestClient(BaseUrl);
var client = new RestClient(BaseUrl);
client.AddDefaultHeaders(headers);

var actual = client.DefaultParameters.Select(x => x as HeaderParameter);
expected.Should().BeSubsetOf(actual);
}

[Fact]
public void AddUrlSegmentWithInt() {
const string name = "foo";

var request = new RestRequest().AddUrlSegment(name, 1);
var actual = request.Parameters.FirstOrDefault(x => x.Name == name);
var expected = new UrlSegmentParameter(name, "1");

expected.Should().BeEquivalentTo(actual);
}
}